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/capture/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/aligned_memory.h"
20 #include "common/angle_version_info.h"
21 #include "common/frame_capture_utils.h"
22 #include "common/gl_enum_utils.h"
23 #include "common/mathutil.h"
24 #include "common/serializer/JsonSerializer.h"
25 #include "common/string_utils.h"
26 #include "common/system_utils.h"
27 #include "gpu_info_util/SystemInfo.h"
28 #include "image_util/storeimage.h"
29 #include "libANGLE/Config.h"
30 #include "libANGLE/Context.h"
31 #include "libANGLE/Context.inl.h"
32 #include "libANGLE/Display.h"
33 #include "libANGLE/EGLSync.h"
34 #include "libANGLE/Fence.h"
35 #include "libANGLE/Framebuffer.h"
36 #include "libANGLE/GLES1Renderer.h"
37 #include "libANGLE/Query.h"
38 #include "libANGLE/ResourceMap.h"
39 #include "libANGLE/Shader.h"
40 #include "libANGLE/Surface.h"
41 #include "libANGLE/VertexArray.h"
42 #include "libANGLE/capture/capture_egl_autogen.h"
43 #include "libANGLE/capture/capture_gles_1_0_autogen.h"
44 #include "libANGLE/capture/capture_gles_2_0_autogen.h"
45 #include "libANGLE/capture/capture_gles_3_0_autogen.h"
46 #include "libANGLE/capture/capture_gles_3_1_autogen.h"
47 #include "libANGLE/capture/capture_gles_3_2_autogen.h"
48 #include "libANGLE/capture/capture_gles_ext_autogen.h"
49 #include "libANGLE/capture/serialize.h"
50 #include "libANGLE/entry_points_utils.h"
51 #include "libANGLE/queryconversions.h"
52 #include "libANGLE/queryutils.h"
53 #include "libANGLE/validationEGL.h"
54 #include "third_party/ceval/ceval.h"
55
56 #define USE_SYSTEM_ZLIB
57 #include "compression_utils_portable.h"
58
59 #if !ANGLE_CAPTURE_ENABLED
60 # error Frame capture must be enabled to include this file.
61 #endif // !ANGLE_CAPTURE_ENABLED
62
63 namespace angle
64 {
65 namespace
66 {
67
68 // TODO: Consolidate to C output and remove option. http://anglebug.com/7753
69
70 constexpr char kEnabledVarName[] = "ANGLE_CAPTURE_ENABLED";
71 constexpr char kOutDirectoryVarName[] = "ANGLE_CAPTURE_OUT_DIR";
72 constexpr char kFrameStartVarName[] = "ANGLE_CAPTURE_FRAME_START";
73 constexpr char kFrameEndVarName[] = "ANGLE_CAPTURE_FRAME_END";
74 constexpr char kTriggerVarName[] = "ANGLE_CAPTURE_TRIGGER";
75 constexpr char kCaptureLabelVarName[] = "ANGLE_CAPTURE_LABEL";
76 constexpr char kCompressionVarName[] = "ANGLE_CAPTURE_COMPRESSION";
77 constexpr char kSerializeStateVarName[] = "ANGLE_CAPTURE_SERIALIZE_STATE";
78 constexpr char kValidationVarName[] = "ANGLE_CAPTURE_VALIDATION";
79 constexpr char kValidationExprVarName[] = "ANGLE_CAPTURE_VALIDATION_EXPR";
80 constexpr char kTrimEnabledVarName[] = "ANGLE_CAPTURE_TRIM_ENABLED";
81 constexpr char kSourceExtVarName[] = "ANGLE_CAPTURE_SOURCE_EXT";
82 constexpr char kSourceSizeVarName[] = "ANGLE_CAPTURE_SOURCE_SIZE";
83 constexpr char kForceShadowVarName[] = "ANGLE_CAPTURE_FORCE_SHADOW";
84
85 constexpr size_t kBinaryAlignment = 16;
86 constexpr size_t kFunctionSizeLimit = 5000;
87
88 // Limit based on MSVC Compiler Error C2026
89 constexpr size_t kStringLengthLimit = 16380;
90
91 // Default limit to number of bytes in a capture source files.
92 constexpr char kDefaultSourceFileExt[] = "cpp";
93 constexpr size_t kDefaultSourceFileSizeThreshold = 400000;
94
95 // Android debug properties that correspond to the above environment variables
96 constexpr char kAndroidEnabled[] = "debug.angle.capture.enabled";
97 constexpr char kAndroidOutDir[] = "debug.angle.capture.out_dir";
98 constexpr char kAndroidFrameStart[] = "debug.angle.capture.frame_start";
99 constexpr char kAndroidFrameEnd[] = "debug.angle.capture.frame_end";
100 constexpr char kAndroidTrigger[] = "debug.angle.capture.trigger";
101 constexpr char kAndroidCaptureLabel[] = "debug.angle.capture.label";
102 constexpr char kAndroidCompression[] = "debug.angle.capture.compression";
103 constexpr char kAndroidValidation[] = "debug.angle.capture.validation";
104 constexpr char kAndroidValidationExpr[] = "debug.angle.capture.validation_expr";
105 constexpr char kAndroidTrimEnabled[] = "debug.angle.capture.trim_enabled";
106 constexpr char kAndroidSourceExt[] = "debug.angle.capture.source_ext";
107 constexpr char kAndroidSourceSize[] = "debug.angle.capture.source_size";
108 constexpr char kAndroidForceShadow[] = "debug.angle.capture.force_shadow";
109
110 struct FramebufferCaptureFuncs
111 {
FramebufferCaptureFuncsangle::__anon9f9064c30111::FramebufferCaptureFuncs112 FramebufferCaptureFuncs(bool isGLES1)
113 {
114 if (isGLES1)
115 {
116 // From GL_OES_framebuffer_object
117 framebufferTexture2D = &gl::CaptureFramebufferTexture2DOES;
118 framebufferRenderbuffer = &gl::CaptureFramebufferRenderbufferOES;
119 bindFramebuffer = &gl::CaptureBindFramebufferOES;
120 genFramebuffers = &gl::CaptureGenFramebuffersOES;
121 bindRenderbuffer = &gl::CaptureBindRenderbufferOES;
122 genRenderbuffers = &gl::CaptureGenRenderbuffersOES;
123 renderbufferStorage = &gl::CaptureRenderbufferStorageOES;
124 }
125 else
126 {
127 framebufferTexture2D = &gl::CaptureFramebufferTexture2D;
128 framebufferRenderbuffer = &gl::CaptureFramebufferRenderbuffer;
129 bindFramebuffer = &gl::CaptureBindFramebuffer;
130 genFramebuffers = &gl::CaptureGenFramebuffers;
131 bindRenderbuffer = &gl::CaptureBindRenderbuffer;
132 genRenderbuffers = &gl::CaptureGenRenderbuffers;
133 renderbufferStorage = &gl::CaptureRenderbufferStorage;
134 }
135 }
136
137 decltype(&gl::CaptureFramebufferTexture2D) framebufferTexture2D;
138 decltype(&gl::CaptureFramebufferRenderbuffer) framebufferRenderbuffer;
139 decltype(&gl::CaptureBindFramebuffer) bindFramebuffer;
140 decltype(&gl::CaptureGenFramebuffers) genFramebuffers;
141 decltype(&gl::CaptureBindRenderbuffer) bindRenderbuffer;
142 decltype(&gl::CaptureGenRenderbuffers) genRenderbuffers;
143 decltype(&gl::CaptureRenderbufferStorage) renderbufferStorage;
144 };
145
146 struct VertexArrayCaptureFuncs
147 {
VertexArrayCaptureFuncsangle::__anon9f9064c30111::VertexArrayCaptureFuncs148 VertexArrayCaptureFuncs(bool isGLES1)
149 {
150 if (isGLES1)
151 {
152 // From GL_OES_vertex_array_object
153 bindVertexArray = &gl::CaptureBindVertexArrayOES;
154 deleteVertexArrays = &gl::CaptureDeleteVertexArraysOES;
155 genVertexArrays = &gl::CaptureGenVertexArraysOES;
156 isVertexArray = &gl::CaptureIsVertexArrayOES;
157 }
158 else
159 {
160 bindVertexArray = &gl::CaptureBindVertexArray;
161 deleteVertexArrays = &gl::CaptureDeleteVertexArrays;
162 genVertexArrays = &gl::CaptureGenVertexArrays;
163 isVertexArray = &gl::CaptureIsVertexArray;
164 }
165 }
166
167 decltype(&gl::CaptureBindVertexArray) bindVertexArray;
168 decltype(&gl::CaptureDeleteVertexArrays) deleteVertexArrays;
169 decltype(&gl::CaptureGenVertexArrays) genVertexArrays;
170 decltype(&gl::CaptureIsVertexArray) isVertexArray;
171 };
172
GetDefaultOutDirectory()173 std::string GetDefaultOutDirectory()
174 {
175 #if defined(ANGLE_PLATFORM_ANDROID)
176 std::string path = "/sdcard/Android/data/";
177
178 // Linux interface to get application id of the running process
179 FILE *cmdline = fopen("/proc/self/cmdline", "r");
180 char applicationId[512];
181 if (cmdline)
182 {
183 fread(applicationId, 1, sizeof(applicationId), cmdline);
184 fclose(cmdline);
185
186 // Some package may have application id as <app_name>:<cmd_name>
187 char *colonSep = strchr(applicationId, ':');
188 if (colonSep)
189 {
190 *colonSep = '\0';
191 }
192 }
193 else
194 {
195 ERR() << "not able to lookup application id";
196 }
197
198 constexpr char kAndroidOutputSubdir[] = "/angle_capture/";
199 path += std::string(applicationId) + kAndroidOutputSubdir;
200
201 // Check for existence of output path
202 struct stat dir_stat;
203 if (stat(path.c_str(), &dir_stat) == -1)
204 {
205 ERR() << "Output directory '" << path
206 << "' does not exist. Create it over adb using mkdir.";
207 }
208
209 return path;
210 #else
211 return std::string("./");
212 #endif // defined(ANGLE_PLATFORM_ANDROID)
213 }
214
GetCaptureTrigger()215 std::string GetCaptureTrigger()
216 {
217 // Use the GetAndSet variant to improve future lookup times
218 return GetAndSetEnvironmentVarOrUnCachedAndroidProperty(kTriggerVarName, kAndroidTrigger);
219 }
220
operator <<(std::ostream & os,gl::ContextID contextId)221 std::ostream &operator<<(std::ostream &os, gl::ContextID contextId)
222 {
223 os << static_cast<int>(contextId.value);
224 return os;
225 }
226
227 // Used to indicate that "shared" should be used to identify the files.
228 constexpr gl::ContextID kSharedContextId = {0};
229 // Used to indicate no context ID should be output.
230 constexpr gl::ContextID kNoContextId = {std::numeric_limits<uint32_t>::max()};
231
232 struct FmtCapturePrefix
233 {
FmtCapturePrefixangle::__anon9f9064c30111::FmtCapturePrefix234 FmtCapturePrefix(gl::ContextID contextIdIn, const std::string &captureLabelIn)
235 : contextId(contextIdIn), captureLabel(captureLabelIn)
236 {}
237 gl::ContextID contextId;
238 const std::string &captureLabel;
239 };
240
operator <<(std::ostream & os,const FmtCapturePrefix & fmt)241 std::ostream &operator<<(std::ostream &os, const FmtCapturePrefix &fmt)
242 {
243 if (fmt.captureLabel.empty())
244 {
245 os << "angle_capture";
246 }
247 else
248 {
249 os << fmt.captureLabel;
250 }
251
252 if (fmt.contextId == kSharedContextId)
253 {
254 os << "_shared";
255 }
256
257 return os;
258 }
259
260 enum class ReplayFunc
261 {
262 Replay,
263 Setup,
264 Reset,
265 };
266
267 constexpr uint32_t kNoPartId = std::numeric_limits<uint32_t>::max();
268
269 // In C, when you declare or define a function that takes no parameters, you must explicitly say the
270 // function takes "void" parameters. When you're calling the function you omit this void. It's
271 // therefore necessary to know how we're using a function to know if we should emi the "void".
272 enum FuncUsage
273 {
274 Prototype,
275 Definition,
276 Call,
277 };
278
operator <<(std::ostream & os,FuncUsage usage)279 std::ostream &operator<<(std::ostream &os, FuncUsage usage)
280 {
281 os << "(";
282 if (usage != FuncUsage::Call)
283 {
284 os << "void";
285 }
286 os << ")";
287 return os;
288 }
289
290 struct FmtReplayFunction
291 {
FmtReplayFunctionangle::__anon9f9064c30111::FmtReplayFunction292 FmtReplayFunction(gl::ContextID contextIdIn,
293 FuncUsage usageIn,
294 uint32_t frameIndexIn,
295 uint32_t partIdIn = kNoPartId)
296 : contextId(contextIdIn), usage(usageIn), frameIndex(frameIndexIn), partId(partIdIn)
297 {}
298 gl::ContextID contextId;
299 FuncUsage usage;
300 uint32_t frameIndex;
301 uint32_t partId;
302 };
303
operator <<(std::ostream & os,const FmtReplayFunction & fmt)304 std::ostream &operator<<(std::ostream &os, const FmtReplayFunction &fmt)
305 {
306 os << "Replay";
307
308 if (fmt.contextId == kSharedContextId)
309 {
310 os << "Shared";
311 }
312
313 os << "Frame" << fmt.frameIndex;
314
315 if (fmt.partId != kNoPartId)
316 {
317 os << "Part" << fmt.partId;
318 }
319 os << fmt.usage;
320 return os;
321 }
322
323 struct FmtSetupFunction
324 {
FmtSetupFunctionangle::__anon9f9064c30111::FmtSetupFunction325 FmtSetupFunction(uint32_t partIdIn, gl::ContextID contextIdIn, FuncUsage usageIn)
326 : partId(partIdIn), contextId(contextIdIn), usage(usageIn)
327 {}
328
329 uint32_t partId;
330 gl::ContextID contextId;
331 FuncUsage usage;
332 };
333
operator <<(std::ostream & os,const FmtSetupFunction & fmt)334 std::ostream &operator<<(std::ostream &os, const FmtSetupFunction &fmt)
335 {
336 os << "SetupReplayContext";
337
338 if (fmt.contextId == kSharedContextId)
339 {
340 os << "Shared";
341 }
342 else
343 {
344 os << fmt.contextId;
345 }
346
347 if (fmt.partId != kNoPartId)
348 {
349 os << "Part" << fmt.partId;
350 }
351 os << fmt.usage;
352 return os;
353 }
354
355 struct FmtResetFunction
356 {
FmtResetFunctionangle::__anon9f9064c30111::FmtResetFunction357 FmtResetFunction(uint32_t partIdIn, gl::ContextID contextIdIn, FuncUsage usageIn)
358 : partId(partIdIn), contextId(contextIdIn), usage(usageIn)
359 {}
360
361 uint32_t partId;
362 gl::ContextID contextId;
363 FuncUsage usage;
364 };
365
operator <<(std::ostream & os,const FmtResetFunction & fmt)366 std::ostream &operator<<(std::ostream &os, const FmtResetFunction &fmt)
367 {
368 os << "ResetReplayContext";
369
370 if (fmt.contextId == kSharedContextId)
371 {
372 os << "Shared";
373 }
374 else
375 {
376 os << fmt.contextId;
377 }
378
379 if (fmt.partId != kNoPartId)
380 {
381 os << "Part" << fmt.partId;
382 }
383 os << fmt.usage;
384 return os;
385 }
386
387 struct FmtFunction
388 {
FmtFunctionangle::__anon9f9064c30111::FmtFunction389 FmtFunction(ReplayFunc funcTypeIn,
390 gl::ContextID contextIdIn,
391 FuncUsage usageIn,
392 uint32_t frameIndexIn,
393 uint32_t partIdIn)
394 : funcType(funcTypeIn),
395 contextId(contextIdIn),
396 usage(usageIn),
397 frameIndex(frameIndexIn),
398 partId(partIdIn)
399 {}
400
401 ReplayFunc funcType;
402 gl::ContextID contextId;
403 FuncUsage usage;
404 uint32_t frameIndex;
405 uint32_t partId;
406 };
407
operator <<(std::ostream & os,const FmtFunction & fmt)408 std::ostream &operator<<(std::ostream &os, const FmtFunction &fmt)
409 {
410 switch (fmt.funcType)
411 {
412 case ReplayFunc::Replay:
413 os << FmtReplayFunction(fmt.contextId, fmt.usage, fmt.frameIndex, fmt.partId);
414 break;
415
416 case ReplayFunc::Setup:
417 os << FmtSetupFunction(fmt.partId, fmt.contextId, fmt.usage);
418 break;
419
420 case ReplayFunc::Reset:
421 os << FmtResetFunction(fmt.partId, fmt.contextId, fmt.usage);
422 break;
423
424 default:
425 UNREACHABLE();
426 break;
427 }
428
429 return os;
430 }
431
432 struct FmtGetSerializedContextStateFunction
433 {
FmtGetSerializedContextStateFunctionangle::__anon9f9064c30111::FmtGetSerializedContextStateFunction434 FmtGetSerializedContextStateFunction(gl::ContextID contextIdIn,
435 FuncUsage usageIn,
436 uint32_t frameIndexIn)
437 : contextId(contextIdIn), usage(usageIn), frameIndex(frameIndexIn)
438 {}
439 gl::ContextID contextId;
440 FuncUsage usage;
441 uint32_t frameIndex;
442 };
443
operator <<(std::ostream & os,const FmtGetSerializedContextStateFunction & fmt)444 std::ostream &operator<<(std::ostream &os, const FmtGetSerializedContextStateFunction &fmt)
445 {
446 os << "GetSerializedContext" << fmt.contextId << "StateFrame" << fmt.frameIndex << "Data"
447 << fmt.usage;
448 return os;
449 }
450
WriteGLFloatValue(std::ostream & out,GLfloat value)451 void WriteGLFloatValue(std::ostream &out, GLfloat value)
452 {
453 // Check for non-representable values
454 ASSERT(std::numeric_limits<float>::has_infinity);
455 ASSERT(std::numeric_limits<float>::has_quiet_NaN);
456
457 if (std::isinf(value))
458 {
459 float negativeInf = -std::numeric_limits<float>::infinity();
460 if (value == negativeInf)
461 {
462 out << "-";
463 }
464 out << "INFINITY";
465 }
466 else if (std::isnan(value))
467 {
468 out << "NAN";
469 }
470 else
471 {
472 out << std::setprecision(16);
473 out << value;
474 }
475 }
476
477 template <typename T, typename CastT = T>
WriteInlineData(const std::vector<uint8_t> & vec,std::ostream & out)478 void WriteInlineData(const std::vector<uint8_t> &vec, std::ostream &out)
479 {
480 const T *data = reinterpret_cast<const T *>(vec.data());
481 size_t count = vec.size() / sizeof(T);
482
483 if (data == nullptr)
484 {
485 return;
486 }
487
488 out << static_cast<CastT>(data[0]);
489
490 for (size_t dataIndex = 1; dataIndex < count; ++dataIndex)
491 {
492 out << ", " << static_cast<CastT>(data[dataIndex]);
493 }
494 }
495
496 template <>
WriteInlineData(const std::vector<uint8_t> & vec,std::ostream & out)497 void WriteInlineData<GLchar>(const std::vector<uint8_t> &vec, std::ostream &out)
498 {
499 const GLchar *data = reinterpret_cast<const GLchar *>(vec.data());
500 size_t count = vec.size() / sizeof(GLchar);
501
502 if (data == nullptr || data[0] == '\0')
503 {
504 return;
505 }
506
507 out << "\"";
508
509 for (size_t dataIndex = 0; dataIndex < count; ++dataIndex)
510 {
511 if (data[dataIndex] == '\0')
512 break;
513
514 out << static_cast<GLchar>(data[dataIndex]);
515 }
516
517 out << "\"";
518 }
519
520 // For compatibility with C, which does not have multi-line string literals, we break strings up
521 // into multiple lines like:
522 //
523 // const char *str[] = {
524 // "multiple\n"
525 // "line\n"
526 // "strings may have \"quotes\"\n"
527 // "and \\slashes\\\n",
528 // };
529 //
530 // Note we need to emit extra escapes to ensure quotes and other special characters are preserved.
531 struct FmtMultiLineString
532 {
FmtMultiLineStringangle::__anon9f9064c30111::FmtMultiLineString533 FmtMultiLineString(const std::string &str) : strings()
534 {
535 std::string str2;
536
537 // Strip any carriage returns before splitting, for consistency
538 if (str.find("\r") != std::string::npos)
539 {
540 // str is const, so have to make a copy of it first
541 str2 = str;
542 ReplaceAllSubstrings(&str2, "\r", "");
543 }
544
545 strings =
546 angle::SplitString(str2.empty() ? str : str2, "\n", WhitespaceHandling::KEEP_WHITESPACE,
547 SplitResult::SPLIT_WANT_ALL);
548 }
549
550 std::vector<std::string> strings;
551 };
552
EscapeString(const std::string & string)553 std::string EscapeString(const std::string &string)
554 {
555 std::stringstream strstr;
556
557 for (char c : string)
558 {
559 if (c == '\"' || c == '\\')
560 {
561 strstr << "\\";
562 }
563 strstr << c;
564 }
565
566 return strstr.str();
567 }
568
operator <<(std::ostream & ostr,const FmtMultiLineString & fmt)569 std::ostream &operator<<(std::ostream &ostr, const FmtMultiLineString &fmt)
570 {
571 ASSERT(!fmt.strings.empty());
572 bool first = true;
573 for (const std::string &string : fmt.strings)
574 {
575 if (first)
576 {
577 first = false;
578 }
579 else
580 {
581 ostr << "\\n\"\n";
582 }
583
584 ostr << "\"" << EscapeString(string);
585 }
586
587 ostr << "\"";
588
589 return ostr;
590 }
591
WriteStringParamReplay(ReplayWriter & replayWriter,std::ostream & out,std::ostream & header,const CallCapture & call,const ParamCapture & param,std::vector<uint8_t> * binaryData)592 void WriteStringParamReplay(ReplayWriter &replayWriter,
593 std::ostream &out,
594 std::ostream &header,
595 const CallCapture &call,
596 const ParamCapture ¶m,
597 std::vector<uint8_t> *binaryData)
598 {
599 const std::vector<uint8_t> &data = param.data[0];
600 // null terminate C style string
601 ASSERT(data.size() > 0 && data.back() == '\0');
602 std::string str(data.begin(), data.end() - 1);
603
604 constexpr size_t kMaxInlineStringLength = 20000;
605 if (str.size() > kMaxInlineStringLength)
606 {
607 // Store in binary file if the string is too long.
608 // Round up to 16-byte boundary for cross ABI safety.
609 size_t offset = rx::roundUpPow2(binaryData->size(), kBinaryAlignment);
610 binaryData->resize(offset + str.size() + 1);
611 memcpy(binaryData->data() + offset, str.data(), str.size() + 1);
612 out << "(const char *)&gBinaryData[" << offset << "]";
613 }
614 else if (str.find('\n') != std::string::npos)
615 {
616 std::string varName = replayWriter.getInlineVariableName(call.entryPoint, param.name);
617 header << "const char " << varName << "[] = \n" << FmtMultiLineString(str) << ";";
618 out << varName;
619 }
620 else
621 {
622 out << "\"" << str << "\"";
623 }
624 }
625
WriteStringPointerParamReplay(ReplayWriter & replayWriter,std::ostream & out,std::ostream & header,const CallCapture & call,const ParamCapture & param)626 void WriteStringPointerParamReplay(ReplayWriter &replayWriter,
627 std::ostream &out,
628 std::ostream &header,
629 const CallCapture &call,
630 const ParamCapture ¶m)
631 {
632 // Concatenate the strings to ensure we get an accurate counter
633 std::vector<std::string> strings;
634 for (const std::vector<uint8_t> &data : param.data)
635 {
636 // null terminate C style string
637 ASSERT(data.size() > 0 && data.back() == '\0');
638 strings.emplace_back(data.begin(), data.end() - 1);
639 }
640
641 bool isNewEntry = false;
642 std::string varName = replayWriter.getInlineStringSetVariableName(call.entryPoint, param.name,
643 strings, &isNewEntry);
644
645 if (isNewEntry)
646 {
647 header << "const char *const " << varName << "[] = { \n";
648
649 for (const std::string &str : strings)
650 {
651 // Break up long strings for MSVC
652 size_t copyLength = 0;
653 std::string separator;
654 for (size_t i = 0; i < str.length(); i += kStringLengthLimit)
655 {
656 if ((str.length() - i) <= kStringLengthLimit)
657 {
658 copyLength = str.length() - i;
659 separator = ",";
660 }
661 else
662 {
663 copyLength = kStringLengthLimit;
664 separator = "";
665 }
666
667 header << FmtMultiLineString(str.substr(i, copyLength)) << separator << "\n";
668 }
669 }
670
671 header << "};\n";
672 }
673
674 out << varName;
675 }
676
677 enum class Indent
678 {
679 Indent,
680 NoIdent,
681 };
682
UpdateResourceIDBuffer(std::ostream & out,Indent indent,size_t bufferIndex,const char * mapName,GLuint resourceID)683 void UpdateResourceIDBuffer(std::ostream &out,
684 Indent indent,
685 size_t bufferIndex,
686 const char *mapName,
687 GLuint resourceID)
688 {
689 if (indent == Indent::Indent)
690 {
691 out << " ";
692 }
693 out << "UpdateResourceIDBuffer(" << bufferIndex << ", g" << mapName << "Map[" << resourceID
694 << "]);\n";
695 }
696
697 template <typename ParamT>
WriteResourceIDPointerParamReplay(ReplayWriter & replayWriter,std::ostream & out,std::ostream & header,const CallCapture & call,const ParamCapture & param,size_t * maxResourceIDBufferSize)698 void WriteResourceIDPointerParamReplay(ReplayWriter &replayWriter,
699 std::ostream &out,
700 std::ostream &header,
701 const CallCapture &call,
702 const ParamCapture ¶m,
703 size_t *maxResourceIDBufferSize)
704 {
705 const ResourceIDType resourceIDType = GetResourceIDTypeFromParamType(param.type);
706 ASSERT(resourceIDType != ResourceIDType::InvalidEnum);
707 const char *name = GetResourceIDTypeName(resourceIDType);
708
709 if (param.dataNElements > 0)
710 {
711 ASSERT(param.data.size() == 1);
712
713 const ParamT *returnedIDs = reinterpret_cast<const ParamT *>(param.data[0].data());
714 for (GLsizei resIndex = 0; resIndex < param.dataNElements; ++resIndex)
715 {
716 ParamT id = returnedIDs[resIndex];
717 UpdateResourceIDBuffer(header, Indent::NoIdent, resIndex, name, id.value);
718 }
719
720 *maxResourceIDBufferSize = std::max<size_t>(*maxResourceIDBufferSize, param.dataNElements);
721 }
722
723 out << "gResourceIDBuffer";
724 }
725
WriteBinaryParamReplay(ReplayWriter & replayWriter,std::ostream & out,std::ostream & header,const CallCapture & call,const ParamCapture & param,std::vector<uint8_t> * binaryData)726 void WriteBinaryParamReplay(ReplayWriter &replayWriter,
727 std::ostream &out,
728 std::ostream &header,
729 const CallCapture &call,
730 const ParamCapture ¶m,
731 std::vector<uint8_t> *binaryData)
732 {
733 std::string varName = replayWriter.getInlineVariableName(call.entryPoint, param.name);
734
735 ASSERT(param.data.size() == 1);
736 const std::vector<uint8_t> &data = param.data[0];
737
738 // Only inline strings (shaders) to simplify the C code.
739 ParamType overrideType = param.type;
740 if (param.type == ParamType::TvoidConstPointer)
741 {
742 overrideType = ParamType::TGLubyteConstPointer;
743 }
744 if (overrideType == ParamType::TGLcharPointer)
745 {
746 // Inline if data is of type string
747 std::string paramTypeString = ParamTypeToString(param.type);
748 header << paramTypeString.substr(0, paramTypeString.length() - 1) << varName << "[] = { ";
749 WriteInlineData<GLchar>(data, header);
750 header << " };\n";
751 out << varName;
752 }
753 else
754 {
755 // Store in binary file if data are not of type string
756 // Round up to 16-byte boundary for cross ABI safety
757 size_t offset = rx::roundUpPow2(binaryData->size(), kBinaryAlignment);
758 binaryData->resize(offset + data.size());
759 memcpy(binaryData->data() + offset, data.data(), data.size());
760 out << "(" << ParamTypeToString(overrideType) << ")&gBinaryData[" << offset << "]";
761 }
762 }
763
WriteCppReplayForCall(const CallCapture & call,ReplayWriter & replayWriter,std::ostream & out,std::ostream & header,std::vector<uint8_t> * binaryData,size_t * maxResourceIDBufferSize)764 void WriteCppReplayForCall(const CallCapture &call,
765 ReplayWriter &replayWriter,
766 std::ostream &out,
767 std::ostream &header,
768 std::vector<uint8_t> *binaryData,
769 size_t *maxResourceIDBufferSize)
770 {
771 std::ostringstream callOut;
772
773 callOut << call.name() << "(";
774
775 bool first = true;
776 for (const ParamCapture ¶m : call.params.getParamCaptures())
777 {
778 if (!first)
779 {
780 callOut << ", ";
781 }
782
783 if (param.arrayClientPointerIndex != -1 && param.value.voidConstPointerVal != nullptr)
784 {
785 callOut << "gClientArrays[" << param.arrayClientPointerIndex << "]";
786 }
787 else if (param.readBufferSizeBytes > 0)
788 {
789 callOut << "(" << ParamTypeToString(param.type) << ")gReadBuffer";
790 }
791 else if (param.data.empty())
792 {
793 if (param.type == ParamType::TGLenum)
794 {
795 OutputGLenumString(callOut, param.enumGroup, param.value.GLenumVal);
796 }
797 else if (param.type == ParamType::TGLbitfield)
798 {
799 OutputGLbitfieldString(callOut, param.enumGroup, param.value.GLbitfieldVal);
800 }
801 else if (param.type == ParamType::TGLfloat)
802 {
803 WriteGLFloatValue(callOut, param.value.GLfloatVal);
804 }
805 else if (param.type == ParamType::TGLsync)
806 {
807 callOut << "gSyncMap[" << FmtPointerIndex(param.value.GLsyncVal) << "]";
808 }
809 else if (param.type == ParamType::TGLuint64 && param.name == "timeout")
810 {
811 if (param.value.GLuint64Val == GL_TIMEOUT_IGNORED)
812 {
813 callOut << "GL_TIMEOUT_IGNORED";
814 }
815 else
816 {
817 WriteParamCaptureReplay(callOut, call, param);
818 }
819 }
820 else
821 {
822 WriteParamCaptureReplay(callOut, call, param);
823 }
824 }
825 else
826 {
827 switch (param.type)
828 {
829 case ParamType::TGLcharConstPointer:
830 WriteStringParamReplay(replayWriter, callOut, header, call, param, binaryData);
831 break;
832 case ParamType::TGLcharConstPointerPointer:
833 WriteStringPointerParamReplay(replayWriter, callOut, header, call, param);
834 break;
835 case ParamType::TBufferIDConstPointer:
836 WriteResourceIDPointerParamReplay<gl::BufferID>(
837 replayWriter, callOut, out, call, param, maxResourceIDBufferSize);
838 break;
839 case ParamType::TFenceNVIDConstPointer:
840 WriteResourceIDPointerParamReplay<gl::FenceNVID>(
841 replayWriter, callOut, out, call, param, maxResourceIDBufferSize);
842 break;
843 case ParamType::TFramebufferIDConstPointer:
844 WriteResourceIDPointerParamReplay<gl::FramebufferID>(
845 replayWriter, callOut, out, call, param, maxResourceIDBufferSize);
846 break;
847 case ParamType::TMemoryObjectIDConstPointer:
848 WriteResourceIDPointerParamReplay<gl::MemoryObjectID>(
849 replayWriter, callOut, out, call, param, maxResourceIDBufferSize);
850 break;
851 case ParamType::TProgramPipelineIDConstPointer:
852 WriteResourceIDPointerParamReplay<gl::ProgramPipelineID>(
853 replayWriter, callOut, out, call, param, maxResourceIDBufferSize);
854 break;
855 case ParamType::TQueryIDConstPointer:
856 WriteResourceIDPointerParamReplay<gl::QueryID>(replayWriter, callOut, out, call,
857 param, maxResourceIDBufferSize);
858 break;
859 case ParamType::TRenderbufferIDConstPointer:
860 WriteResourceIDPointerParamReplay<gl::RenderbufferID>(
861 replayWriter, callOut, out, call, param, maxResourceIDBufferSize);
862 break;
863 case ParamType::TSamplerIDConstPointer:
864 WriteResourceIDPointerParamReplay<gl::SamplerID>(
865 replayWriter, callOut, out, call, param, maxResourceIDBufferSize);
866 break;
867 case ParamType::TSemaphoreIDConstPointer:
868 WriteResourceIDPointerParamReplay<gl::SemaphoreID>(
869 replayWriter, callOut, out, call, param, maxResourceIDBufferSize);
870 break;
871 case ParamType::TTextureIDConstPointer:
872 WriteResourceIDPointerParamReplay<gl::TextureID>(
873 replayWriter, callOut, out, call, param, maxResourceIDBufferSize);
874 break;
875 case ParamType::TTransformFeedbackIDConstPointer:
876 WriteResourceIDPointerParamReplay<gl::TransformFeedbackID>(
877 replayWriter, callOut, out, call, param, maxResourceIDBufferSize);
878 break;
879 case ParamType::TVertexArrayIDConstPointer:
880 WriteResourceIDPointerParamReplay<gl::VertexArrayID>(
881 replayWriter, callOut, out, call, param, maxResourceIDBufferSize);
882 break;
883 default:
884 WriteBinaryParamReplay(replayWriter, callOut, header, call, param, binaryData);
885 break;
886 }
887 }
888
889 first = false;
890 }
891
892 callOut << ")";
893
894 out << callOut.str();
895 }
896
MaxClientArraySize(const gl::AttribArray<size_t> & clientArraySizes)897 size_t MaxClientArraySize(const gl::AttribArray<size_t> &clientArraySizes)
898 {
899 size_t found = 0;
900 for (size_t size : clientArraySizes)
901 {
902 if (size > found)
903 {
904 found = size;
905 }
906 }
907
908 return found;
909 }
910
GetBinaryDataFilePath(bool compression,const std::string & captureLabel)911 std::string GetBinaryDataFilePath(bool compression, const std::string &captureLabel)
912 {
913 std::stringstream fnameStream;
914 fnameStream << FmtCapturePrefix(kNoContextId, captureLabel) << ".angledata";
915 if (compression)
916 {
917 fnameStream << ".gz";
918 }
919 return fnameStream.str();
920 }
921
SaveBinaryData(bool compression,const std::string & outDir,gl::ContextID contextId,const std::string & captureLabel,const std::vector<uint8_t> & binaryData)922 void SaveBinaryData(bool compression,
923 const std::string &outDir,
924 gl::ContextID contextId,
925 const std::string &captureLabel,
926 const std::vector<uint8_t> &binaryData)
927 {
928 std::string binaryDataFileName = GetBinaryDataFilePath(compression, captureLabel);
929 std::string dataFilepath = outDir + binaryDataFileName;
930
931 SaveFileHelper saveData(dataFilepath);
932
933 if (compression)
934 {
935 // Save compressed data.
936 uLong uncompressedSize = static_cast<uLong>(binaryData.size());
937 uLong expectedCompressedSize = zlib_internal::GzipExpectedCompressedSize(uncompressedSize);
938
939 std::vector<uint8_t> compressedData(expectedCompressedSize, 0);
940
941 uLong compressedSize = expectedCompressedSize;
942 int zResult = zlib_internal::GzipCompressHelper(compressedData.data(), &compressedSize,
943 binaryData.data(), uncompressedSize,
944 nullptr, nullptr);
945
946 if (zResult != Z_OK)
947 {
948 FATAL() << "Error compressing binary data: " << zResult;
949 }
950
951 saveData.write(compressedData.data(), compressedSize);
952 }
953 else
954 {
955 saveData.write(binaryData.data(), binaryData.size());
956 }
957 }
958
WriteInitReplayCall(bool compression,std::ostream & out,gl::ContextID contextID,const std::string & captureLabel,size_t maxClientArraySize,size_t readBufferSize,size_t resourceIDBufferSize,const PackedEnumMap<ResourceIDType,uint32_t> & maxIDs)959 void WriteInitReplayCall(bool compression,
960 std::ostream &out,
961 gl::ContextID contextID,
962 const std::string &captureLabel,
963 size_t maxClientArraySize,
964 size_t readBufferSize,
965 size_t resourceIDBufferSize,
966 const PackedEnumMap<ResourceIDType, uint32_t> &maxIDs)
967 {
968 std::string binaryDataFileName = GetBinaryDataFilePath(compression, captureLabel);
969
970 out << " // binaryDataFileName = " << binaryDataFileName << "\n";
971 out << " // maxClientArraySize = " << maxClientArraySize << "\n";
972 out << " // maxClientArraySize = " << maxClientArraySize << "\n";
973 out << " // readBufferSize = " << readBufferSize << "\n";
974 out << " // resourceIDBufferSize = " << resourceIDBufferSize << "\n";
975 out << " // contextID = " << contextID << "\n";
976
977 for (ResourceIDType resourceID : AllEnums<ResourceIDType>())
978 {
979 const char *name = GetResourceIDTypeName(resourceID);
980 out << " // max" << name << " = " << maxIDs[resourceID] << "\n";
981 }
982
983 out << " InitializeReplay4(\"" << binaryDataFileName << "\", " << maxClientArraySize << ", "
984 << readBufferSize << ", " << resourceIDBufferSize << ", " << contextID;
985
986 for (ResourceIDType resourceID : AllEnums<ResourceIDType>())
987 {
988 out << ", " << maxIDs[resourceID];
989 }
990
991 out << ");\n";
992 }
993
DeleteResourcesInReset(std::stringstream & out,const ResourceSet & newResources,const ResourceSet & resourcesToDelete,const char * resourceName,size_t * maxResourceIDBufferSize)994 void DeleteResourcesInReset(std::stringstream &out,
995 const ResourceSet &newResources,
996 const ResourceSet &resourcesToDelete,
997 const char *resourceName,
998 size_t *maxResourceIDBufferSize)
999 {
1000 if (!newResources.empty() || !resourcesToDelete.empty())
1001 {
1002 size_t count = 0;
1003
1004 for (GLuint oldResource : resourcesToDelete)
1005 {
1006 UpdateResourceIDBuffer(out, Indent::Indent, count++, resourceName, oldResource);
1007 }
1008
1009 for (GLuint newResource : newResources)
1010 {
1011 UpdateResourceIDBuffer(out, Indent::Indent, count++, resourceName, newResource);
1012 }
1013
1014 // Delete all the new and old buffers at once
1015 out << " glDelete" << resourceName << "s(" << count << ", gResourceIDBuffer);\n";
1016
1017 *maxResourceIDBufferSize = std::max(*maxResourceIDBufferSize, count);
1018 }
1019 }
1020
1021 // TODO (http://anglebug.com/4599): Reset more state on frame loop
MaybeResetResources(gl::ContextID contextID,ResourceIDType resourceIDType,ReplayWriter & replayWriter,std::stringstream & out,std::stringstream & header,ResourceTracker * resourceTracker,std::vector<uint8_t> * binaryData,bool & anyResourceReset,size_t * maxResourceIDBufferSize)1022 void MaybeResetResources(gl::ContextID contextID,
1023 ResourceIDType resourceIDType,
1024 ReplayWriter &replayWriter,
1025 std::stringstream &out,
1026 std::stringstream &header,
1027 ResourceTracker *resourceTracker,
1028 std::vector<uint8_t> *binaryData,
1029 bool &anyResourceReset,
1030 size_t *maxResourceIDBufferSize)
1031 {
1032 // Track the initial output position so we can detect if it has moved
1033 std::streampos initialOutPos = out.tellp();
1034
1035 switch (resourceIDType)
1036 {
1037 case ResourceIDType::Buffer:
1038 {
1039 TrackedResource &trackedBuffers =
1040 resourceTracker->getTrackedResource(contextID, ResourceIDType::Buffer);
1041 ResourceSet &newBuffers = trackedBuffers.getNewResources();
1042 ResourceSet &buffersToDelete = trackedBuffers.getResourcesToDelete();
1043 ResourceSet &buffersToRegen = trackedBuffers.getResourcesToRegen();
1044 ResourceCalls &bufferRegenCalls = trackedBuffers.getResourceRegenCalls();
1045 ResourceCalls &bufferRestoreCalls = trackedBuffers.getResourceRestoreCalls();
1046
1047 BufferCalls &bufferMapCalls = resourceTracker->getBufferMapCalls();
1048 BufferCalls &bufferUnmapCalls = resourceTracker->getBufferUnmapCalls();
1049
1050 DeleteResourcesInReset(out, newBuffers, buffersToDelete, "Buffer",
1051 maxResourceIDBufferSize);
1052
1053 // If any of our starting buffers were deleted during the run, recreate them
1054 for (GLuint id : buffersToRegen)
1055 {
1056 // Emit their regen calls
1057 for (CallCapture &call : bufferRegenCalls[id])
1058 {
1059 out << " ";
1060 WriteCppReplayForCall(call, replayWriter, out, header, binaryData,
1061 maxResourceIDBufferSize);
1062 out << ";\n";
1063 }
1064 }
1065
1066 // If any of our starting buffers were modified during the run, restore their contents
1067 ResourceSet &buffersToRestore = trackedBuffers.getResourcesToRestore();
1068 for (GLuint id : buffersToRestore)
1069 {
1070 if (resourceTracker->getStartingBuffersMappedCurrent(id))
1071 {
1072 // Some drivers require the buffer to be unmapped before you can update data,
1073 // which violates the spec. See gl::Buffer::bufferDataImpl().
1074 for (CallCapture &call : bufferUnmapCalls[id])
1075 {
1076 out << " ";
1077 WriteCppReplayForCall(call, replayWriter, out, header, binaryData,
1078 maxResourceIDBufferSize);
1079 out << ";\n";
1080 }
1081 }
1082
1083 // Emit their restore calls
1084 for (CallCapture &call : bufferRestoreCalls[id])
1085 {
1086 out << " ";
1087 WriteCppReplayForCall(call, replayWriter, out, header, binaryData,
1088 maxResourceIDBufferSize);
1089 out << ";\n";
1090
1091 // Also note that this buffer has been implicitly unmapped by this call
1092 resourceTracker->setBufferUnmapped(contextID, id);
1093 }
1094 }
1095
1096 // Update the map/unmap of buffers to match the starting state
1097 ResourceSet startingBuffers = trackedBuffers.getStartingResources();
1098 for (GLuint id : startingBuffers)
1099 {
1100 // If the buffer was mapped at the start, but is not mapped now, we need to map
1101 if (resourceTracker->getStartingBuffersMappedInitial(id) &&
1102 !resourceTracker->getStartingBuffersMappedCurrent(id))
1103 {
1104 // Emit their map calls
1105 for (CallCapture &call : bufferMapCalls[id])
1106 {
1107 out << " ";
1108 WriteCppReplayForCall(call, replayWriter, out, header, binaryData,
1109 maxResourceIDBufferSize);
1110 out << ";\n";
1111 }
1112 }
1113 // If the buffer was unmapped at the start, but is mapped now, we need to unmap
1114 if (!resourceTracker->getStartingBuffersMappedInitial(id) &&
1115 resourceTracker->getStartingBuffersMappedCurrent(id))
1116 {
1117 // Emit their unmap calls
1118 for (CallCapture &call : bufferUnmapCalls[id])
1119 {
1120 out << " ";
1121 WriteCppReplayForCall(call, replayWriter, out, header, binaryData,
1122 maxResourceIDBufferSize);
1123 out << ";\n";
1124 }
1125 }
1126 }
1127
1128 // Restore buffer bindings as seen during MEC
1129 std::vector<CallCapture> &bufferBindingCalls = resourceTracker->getBufferBindingCalls();
1130 for (CallCapture &call : bufferBindingCalls)
1131 {
1132 out << " ";
1133 WriteCppReplayForCall(call, replayWriter, out, header, binaryData,
1134 maxResourceIDBufferSize);
1135 out << ";\n";
1136 }
1137
1138 break;
1139 }
1140 case ResourceIDType::Framebuffer:
1141 {
1142 TrackedResource &trackedFramebuffers =
1143 resourceTracker->getTrackedResource(contextID, ResourceIDType::Framebuffer);
1144 ResourceSet &newFramebuffers = trackedFramebuffers.getNewResources();
1145 ResourceSet &framebuffersToDelete = trackedFramebuffers.getResourcesToDelete();
1146 ResourceSet &framebuffersToRegen = trackedFramebuffers.getResourcesToRegen();
1147 ResourceCalls &framebufferRegenCalls = trackedFramebuffers.getResourceRegenCalls();
1148 ResourceCalls &framebufferRestoreCalls = trackedFramebuffers.getResourceRestoreCalls();
1149
1150 DeleteResourcesInReset(out, newFramebuffers, framebuffersToDelete, "Framebuffer",
1151 maxResourceIDBufferSize);
1152
1153 for (GLuint id : framebuffersToRegen)
1154 {
1155 // Emit their regen calls
1156 for (CallCapture &call : framebufferRegenCalls[id])
1157 {
1158 out << " ";
1159 WriteCppReplayForCall(call, replayWriter, out, header, binaryData,
1160 maxResourceIDBufferSize);
1161 out << ";\n";
1162 }
1163 }
1164
1165 // If any of our starting framebuffers were modified during the run, restore their
1166 // contents
1167 ResourceSet &framebuffersToRestore = trackedFramebuffers.getResourcesToRestore();
1168 for (GLuint id : framebuffersToRestore)
1169 {
1170 // Emit their restore calls
1171 for (CallCapture &call : framebufferRestoreCalls[id])
1172 {
1173 out << " ";
1174 WriteCppReplayForCall(call, replayWriter, out, header, binaryData,
1175 maxResourceIDBufferSize);
1176 out << ";\n";
1177 }
1178 }
1179 break;
1180 }
1181 case ResourceIDType::Renderbuffer:
1182 {
1183 TrackedResource &trackedRenderbuffers =
1184 resourceTracker->getTrackedResource(contextID, ResourceIDType::Renderbuffer);
1185 ResourceSet &newRenderbuffers = trackedRenderbuffers.getNewResources();
1186 ResourceSet &renderbuffersToDelete = trackedRenderbuffers.getResourcesToDelete();
1187 ResourceSet &renderbuffersToRegen = trackedRenderbuffers.getResourcesToRegen();
1188 ResourceCalls &renderbufferRegenCalls = trackedRenderbuffers.getResourceRegenCalls();
1189 ResourceCalls &renderbufferRestoreCalls =
1190 trackedRenderbuffers.getResourceRestoreCalls();
1191
1192 DeleteResourcesInReset(out, newRenderbuffers, renderbuffersToDelete, "Renderbuffer",
1193 maxResourceIDBufferSize);
1194
1195 for (GLuint id : renderbuffersToRegen)
1196 {
1197 // Emit their regen calls
1198 for (CallCapture &call : renderbufferRegenCalls[id])
1199 {
1200 out << " ";
1201 WriteCppReplayForCall(call, replayWriter, out, header, binaryData,
1202 maxResourceIDBufferSize);
1203 out << ";\n";
1204 }
1205 }
1206
1207 // If any of our starting renderbuffers were modified during the run, restore their
1208 // contents
1209 ResourceSet &renderbuffersToRestore = trackedRenderbuffers.getResourcesToRestore();
1210 for (GLuint id : renderbuffersToRestore)
1211 {
1212 // Emit their restore calls
1213 for (CallCapture &call : renderbufferRestoreCalls[id])
1214 {
1215 out << " ";
1216 WriteCppReplayForCall(call, replayWriter, out, header, binaryData,
1217 maxResourceIDBufferSize);
1218 out << ";\n";
1219 }
1220 }
1221 break;
1222 }
1223 case ResourceIDType::ShaderProgram:
1224 {
1225 TrackedResource &trackedShaderPrograms =
1226 resourceTracker->getTrackedResource(contextID, ResourceIDType::ShaderProgram);
1227 ResourceSet &newShaderPrograms = trackedShaderPrograms.getNewResources();
1228 ResourceSet &shaderProgramsToDelete = trackedShaderPrograms.getResourcesToDelete();
1229 ResourceSet &shaderProgramsToRegen = trackedShaderPrograms.getResourcesToRegen();
1230 ResourceSet &shaderProgramsToRestore = trackedShaderPrograms.getResourcesToRestore();
1231 ResourceCalls &shaderProgramRegenCalls = trackedShaderPrograms.getResourceRegenCalls();
1232 ResourceCalls &shaderProgramRestoreCalls =
1233 trackedShaderPrograms.getResourceRestoreCalls();
1234
1235 // If we have any new shaders or programs created and not deleted during the run, delete
1236 // them now
1237 for (const GLuint &newShaderProgram : newShaderPrograms)
1238 {
1239 if (resourceTracker->getShaderProgramType({newShaderProgram}) ==
1240 ShaderProgramType::ShaderType)
1241 {
1242 out << " glDeleteShader(gShaderProgramMap[" << newShaderProgram << "]);\n";
1243 }
1244 else
1245 {
1246 ASSERT(resourceTracker->getShaderProgramType({newShaderProgram}) ==
1247 ShaderProgramType::ProgramType);
1248 out << " glDeleteProgram(gShaderProgramMap[" << newShaderProgram << "]);\n";
1249 }
1250 }
1251
1252 // Do the same for shaders/programs to be deleted
1253 for (const GLuint &shaderProgramToDelete : shaderProgramsToDelete)
1254 {
1255 if (resourceTracker->getShaderProgramType({shaderProgramToDelete}) ==
1256 ShaderProgramType::ShaderType)
1257 {
1258 out << " glDeleteShader(gShaderProgramMap[" << shaderProgramToDelete
1259 << "]);\n";
1260 }
1261 else
1262 {
1263 ASSERT(resourceTracker->getShaderProgramType({shaderProgramToDelete}) ==
1264 ShaderProgramType::ProgramType);
1265 out << " glDeleteProgram(gShaderProgramMap[" << shaderProgramToDelete
1266 << "]);\n";
1267 }
1268 }
1269
1270 for (const GLuint id : shaderProgramsToRegen)
1271 {
1272 // Emit their regen calls
1273 for (CallCapture &call : shaderProgramRegenCalls[id])
1274 {
1275 out << " ";
1276 WriteCppReplayForCall(call, replayWriter, out, header, binaryData,
1277 maxResourceIDBufferSize);
1278 out << ";\n";
1279 }
1280 }
1281
1282 for (const GLuint id : shaderProgramsToRestore)
1283 {
1284 // Emit their restore calls
1285 for (CallCapture &call : shaderProgramRestoreCalls[id])
1286 {
1287 out << " ";
1288 WriteCppReplayForCall(call, replayWriter, out, header, binaryData,
1289 maxResourceIDBufferSize);
1290 out << ";\n";
1291 }
1292 }
1293
1294 break;
1295 }
1296 case ResourceIDType::Texture:
1297 {
1298 TrackedResource &trackedTextures =
1299 resourceTracker->getTrackedResource(contextID, ResourceIDType::Texture);
1300 ResourceSet &newTextures = trackedTextures.getNewResources();
1301 ResourceSet &texturesToDelete = trackedTextures.getResourcesToDelete();
1302 ResourceSet &texturesToRegen = trackedTextures.getResourcesToRegen();
1303 ResourceCalls &textureRegenCalls = trackedTextures.getResourceRegenCalls();
1304 ResourceCalls &textureRestoreCalls = trackedTextures.getResourceRestoreCalls();
1305
1306 DeleteResourcesInReset(out, newTextures, texturesToDelete, "Texture",
1307 maxResourceIDBufferSize);
1308
1309 // If any of our starting textures were deleted, regen them
1310 for (GLuint id : texturesToRegen)
1311 {
1312 // Emit their regen calls
1313 for (CallCapture &call : textureRegenCalls[id])
1314 {
1315 out << " ";
1316 WriteCppReplayForCall(call, replayWriter, out, header, binaryData,
1317 maxResourceIDBufferSize);
1318 out << ";\n";
1319 }
1320 }
1321
1322 // If any of our starting textures were modified during the run, restore their contents
1323 ResourceSet &texturesToRestore = trackedTextures.getResourcesToRestore();
1324 for (GLuint id : texturesToRestore)
1325 {
1326 // Emit their restore calls
1327 for (CallCapture &call : textureRestoreCalls[id])
1328 {
1329 out << " ";
1330 WriteCppReplayForCall(call, replayWriter, out, header, binaryData,
1331 maxResourceIDBufferSize);
1332 out << ";\n";
1333 }
1334 }
1335 break;
1336 }
1337 case ResourceIDType::VertexArray:
1338 {
1339 TrackedResource &trackedVertexArrays =
1340 resourceTracker->getTrackedResource(contextID, ResourceIDType::VertexArray);
1341 ResourceSet &newVertexArrays = trackedVertexArrays.getNewResources();
1342 ResourceSet &vertexArraysToDelete = trackedVertexArrays.getResourcesToDelete();
1343 ResourceSet &vertexArraysToRegen = trackedVertexArrays.getResourcesToRegen();
1344 ResourceSet &vertexArraysToRestore = trackedVertexArrays.getResourcesToRestore();
1345 ResourceCalls &vertexArrayRegenCalls = trackedVertexArrays.getResourceRegenCalls();
1346 ResourceCalls &vertexArrayRestoreCalls = trackedVertexArrays.getResourceRestoreCalls();
1347
1348 DeleteResourcesInReset(out, newVertexArrays, vertexArraysToDelete, "VertexArray",
1349 maxResourceIDBufferSize);
1350
1351 // If any of our starting vertex arrays were deleted during the run, recreate them
1352 for (GLuint id : vertexArraysToRegen)
1353 {
1354 // Emit their regen calls
1355 for (CallCapture &call : vertexArrayRegenCalls[id])
1356 {
1357 out << " ";
1358 WriteCppReplayForCall(call, replayWriter, out, header, binaryData,
1359 maxResourceIDBufferSize);
1360 out << ";\n";
1361 }
1362 }
1363
1364 // If any of our starting vertex arrays were modified during the run, restore their
1365 // contents
1366 for (GLuint id : vertexArraysToRestore)
1367 {
1368 // Emit their restore calls
1369 for (CallCapture &call : vertexArrayRestoreCalls[id])
1370 {
1371 out << " ";
1372 WriteCppReplayForCall(call, replayWriter, out, header, binaryData,
1373 maxResourceIDBufferSize);
1374 out << ";\n";
1375 }
1376 }
1377 break;
1378 }
1379 case ResourceIDType::egl_Sync:
1380 {
1381 TrackedResource &trackedEGLSyncs =
1382 resourceTracker->getTrackedResource(contextID, ResourceIDType::egl_Sync);
1383 ResourceSet &newEGLSyncs = trackedEGLSyncs.getNewResources();
1384 ResourceSet &eglSyncsToDelete = trackedEGLSyncs.getResourcesToDelete();
1385 ResourceSet &eglSyncsToRegen = trackedEGLSyncs.getResourcesToRegen();
1386 ResourceCalls &eglSyncRegenCalls = trackedEGLSyncs.getResourceRegenCalls();
1387
1388 if (!newEGLSyncs.empty() || !eglSyncsToDelete.empty())
1389 {
1390 for (GLuint oldResource : eglSyncsToDelete)
1391 {
1392 out << " eglDestroySyncKHR(gEGLDisplay, gEGLSyncMap[" << oldResource
1393 << "]);\n";
1394 }
1395
1396 for (GLuint newResource : newEGLSyncs)
1397 {
1398 out << " eglDestroySyncKHR(gEGLDisplay, gEGLSyncMap[" << newResource
1399 << "]);\n";
1400 }
1401 }
1402
1403 // If any of our starting EGLsyncs were deleted during the run, recreate them
1404 for (GLuint id : eglSyncsToRegen)
1405 {
1406 // Emit their regen calls
1407 for (CallCapture &call : eglSyncRegenCalls[id])
1408 {
1409 out << " ";
1410 WriteCppReplayForCall(call, replayWriter, out, header, binaryData,
1411 maxResourceIDBufferSize);
1412 out << ";\n";
1413 }
1414 }
1415 break;
1416 }
1417 default:
1418 // TODO (http://anglebug.com/4599): Reset more resource types
1419 break;
1420 }
1421
1422 // If the output position has moved, we Reset something
1423 anyResourceReset = (initialOutPos != out.tellp());
1424 }
1425
MaybeResetFenceSyncObjects(std::stringstream & out,ReplayWriter & replayWriter,std::stringstream & header,ResourceTracker * resourceTracker,std::vector<uint8_t> * binaryData,size_t * maxResourceIDBufferSize)1426 void MaybeResetFenceSyncObjects(std::stringstream &out,
1427 ReplayWriter &replayWriter,
1428 std::stringstream &header,
1429 ResourceTracker *resourceTracker,
1430 std::vector<uint8_t> *binaryData,
1431 size_t *maxResourceIDBufferSize)
1432 {
1433 FenceSyncCalls &fenceSyncRegenCalls = resourceTracker->getFenceSyncRegenCalls();
1434
1435 // If any of our starting fence sync objects were deleted during the run, recreate them
1436 FenceSyncSet &fenceSyncsToRegen = resourceTracker->getFenceSyncsToRegen();
1437 for (const gl::SyncID syncID : fenceSyncsToRegen)
1438 {
1439 // Emit their regen calls
1440 for (CallCapture &call : fenceSyncRegenCalls[syncID])
1441 {
1442 out << " ";
1443 WriteCppReplayForCall(call, replayWriter, out, header, binaryData,
1444 maxResourceIDBufferSize);
1445 out << ";\n";
1446 }
1447 }
1448 }
1449
Capture(std::vector<CallCapture> * setupCalls,CallCapture && call)1450 void Capture(std::vector<CallCapture> *setupCalls, CallCapture &&call)
1451 {
1452 setupCalls->emplace_back(std::move(call));
1453 }
1454
CaptureUpdateCurrentProgram(const CallCapture & call,int programParamPos,std::vector<CallCapture> * callsOut)1455 void CaptureUpdateCurrentProgram(const CallCapture &call,
1456 int programParamPos,
1457 std::vector<CallCapture> *callsOut)
1458 {
1459 const ParamCapture ¶m =
1460 call.params.getParam("programPacked", ParamType::TShaderProgramID, programParamPos);
1461 gl::ShaderProgramID programID = param.value.ShaderProgramIDVal;
1462
1463 ParamBuffer paramBuffer;
1464 paramBuffer.addValueParam("program", ParamType::TGLuint, programID.value);
1465
1466 callsOut->emplace_back("UpdateCurrentProgram", std::move(paramBuffer));
1467 }
1468
ProgramNeedsReset(const gl::ContextID contextID,ResourceTracker * resourceTracker,gl::ShaderProgramID programID)1469 bool ProgramNeedsReset(const gl::ContextID contextID,
1470 ResourceTracker *resourceTracker,
1471 gl::ShaderProgramID programID)
1472 {
1473 // Check whether the program is listed in programs to regen or restore
1474 TrackedResource &trackedShaderPrograms =
1475 resourceTracker->getTrackedResource(contextID, ResourceIDType::ShaderProgram);
1476
1477 ResourceSet &shaderProgramsToRegen = trackedShaderPrograms.getResourcesToRegen();
1478 if (shaderProgramsToRegen.count(programID.value) != 0)
1479 {
1480 return true;
1481 }
1482
1483 ResourceSet &shaderProgramsToRestore = trackedShaderPrograms.getResourcesToRestore();
1484 if (shaderProgramsToRestore.count(programID.value) != 0)
1485 {
1486 return true;
1487 }
1488
1489 return false;
1490 }
1491
MaybeResetDefaultUniforms(std::stringstream & out,ReplayWriter & replayWriter,std::stringstream & header,const gl::Context * context,ResourceTracker * resourceTracker,std::vector<uint8_t> * binaryData,size_t * maxResourceIDBufferSize)1492 void MaybeResetDefaultUniforms(std::stringstream &out,
1493 ReplayWriter &replayWriter,
1494 std::stringstream &header,
1495 const gl::Context *context,
1496 ResourceTracker *resourceTracker,
1497 std::vector<uint8_t> *binaryData,
1498 size_t *maxResourceIDBufferSize)
1499 {
1500 DefaultUniformLocationsPerProgramMap &defaultUniformsToReset =
1501 resourceTracker->getDefaultUniformsToReset();
1502
1503 for (const auto &uniformIter : defaultUniformsToReset)
1504 {
1505 gl::ShaderProgramID programID = uniformIter.first;
1506 const DefaultUniformLocationsSet &locations = uniformIter.second;
1507
1508 if (ProgramNeedsReset(context->id(), resourceTracker, programID))
1509 {
1510 // Skip programs marked for reset as they will update their own uniforms
1511 return;
1512 }
1513
1514 // Bind the program to update its uniforms
1515 std::vector<CallCapture> bindCalls;
1516 Capture(&bindCalls, CaptureUseProgram(context->getState(), true, programID));
1517 CaptureUpdateCurrentProgram((&bindCalls)->back(), 0, &bindCalls);
1518 for (CallCapture &call : bindCalls)
1519 {
1520 out << " ";
1521 WriteCppReplayForCall(call, replayWriter, out, header, binaryData,
1522 maxResourceIDBufferSize);
1523 out << ";\n";
1524 }
1525
1526 DefaultUniformCallsPerLocationMap &defaultUniformResetCalls =
1527 resourceTracker->getDefaultUniformResetCalls(programID);
1528
1529 // Uniform arrays might have been modified in the middle (i.e. location 5 out of 10)
1530 // We only have Reset calls for the entire array, so emit them once for the entire array
1531 std::set<gl::UniformLocation> alreadyReset;
1532
1533 // Emit the reset calls per modified location
1534 for (const gl::UniformLocation &location : locations)
1535 {
1536 gl::UniformLocation baseLocation =
1537 resourceTracker->getDefaultUniformBaseLocation(programID, location);
1538 if (alreadyReset.find(baseLocation) != alreadyReset.end())
1539 {
1540 // We've already Reset this array
1541 continue;
1542 }
1543 alreadyReset.insert(baseLocation);
1544
1545 ASSERT(defaultUniformResetCalls.find(baseLocation) != defaultUniformResetCalls.end());
1546 std::vector<CallCapture> &callsPerLocation = defaultUniformResetCalls[baseLocation];
1547
1548 for (CallCapture &call : callsPerLocation)
1549 {
1550 out << " ";
1551 WriteCppReplayForCall(call, replayWriter, out, header, binaryData,
1552 maxResourceIDBufferSize);
1553 out << ";\n";
1554 }
1555 }
1556 }
1557 }
1558
MaybeResetOpaqueTypeObjects(ReplayWriter & replayWriter,std::stringstream & out,std::stringstream & header,const gl::Context * context,ResourceTracker * resourceTracker,std::vector<uint8_t> * binaryData,size_t * maxResourceIDBufferSize)1559 void MaybeResetOpaqueTypeObjects(ReplayWriter &replayWriter,
1560 std::stringstream &out,
1561 std::stringstream &header,
1562 const gl::Context *context,
1563 ResourceTracker *resourceTracker,
1564 std::vector<uint8_t> *binaryData,
1565 size_t *maxResourceIDBufferSize)
1566 {
1567 MaybeResetFenceSyncObjects(out, replayWriter, header, resourceTracker, binaryData,
1568 maxResourceIDBufferSize);
1569
1570 MaybeResetDefaultUniforms(out, replayWriter, header, context, resourceTracker, binaryData,
1571 maxResourceIDBufferSize);
1572 }
1573
MaybeResetContextState(ReplayWriter & replayWriter,std::stringstream & out,std::stringstream & header,ResourceTracker * resourceTracker,const gl::Context * context,std::vector<uint8_t> * binaryData,StateResetHelper & stateResetHelper,size_t * maxResourceIDBufferSize)1574 void MaybeResetContextState(ReplayWriter &replayWriter,
1575 std::stringstream &out,
1576 std::stringstream &header,
1577 ResourceTracker *resourceTracker,
1578 const gl::Context *context,
1579 std::vector<uint8_t> *binaryData,
1580 StateResetHelper &stateResetHelper,
1581 size_t *maxResourceIDBufferSize)
1582 {
1583 // Check dirty states per entrypoint
1584 for (const EntryPoint &entryPoint : stateResetHelper.getDirtyEntryPoints())
1585 {
1586 const CallResetMap *resetCalls = &stateResetHelper.getResetCalls();
1587
1588 // Create the default reset call for this entrypoint
1589 if (resetCalls->find(entryPoint) == resetCalls->end())
1590 {
1591 // If we don't have any reset calls for these entrypoints, that means we started capture
1592 // from the beginning, amd mid-execution capture was not invoked.
1593 stateResetHelper.setDefaultResetCalls(context, entryPoint);
1594 }
1595
1596 // Emit the calls
1597 for (const auto &call : resetCalls->at(entryPoint))
1598 {
1599 out << " ";
1600 WriteCppReplayForCall(call, replayWriter, out, header, binaryData,
1601 maxResourceIDBufferSize);
1602 out << ";\n";
1603 }
1604 }
1605 }
1606
MarkResourceIDActive(ResourceIDType resourceType,GLuint id,std::vector<CallCapture> * setupCalls,const ResourceIDToSetupCallsMap * resourceIDToSetupCallsMap)1607 void MarkResourceIDActive(ResourceIDType resourceType,
1608 GLuint id,
1609 std::vector<CallCapture> *setupCalls,
1610 const ResourceIDToSetupCallsMap *resourceIDToSetupCallsMap)
1611 {
1612 const std::map<GLuint, gl::Range<size_t>> &resourceSetupCalls =
1613 (*resourceIDToSetupCallsMap)[resourceType];
1614 const auto iter = resourceSetupCalls.find(id);
1615 if (iter == resourceSetupCalls.end())
1616 {
1617 return;
1618 }
1619
1620 // Mark all of the calls that were used to initialize this resource as ACTIVE
1621 const gl::Range<size_t> &calls = iter->second;
1622 for (size_t index : calls)
1623 {
1624 (*setupCalls)[index].isActive = true;
1625 }
1626 }
1627
1628 // Some replay functions can get quite large. If over a certain size, this method breaks up the
1629 // function into parts to avoid overflowing the stack and causing slow compilation.
WriteCppReplayFunctionWithParts(const gl::ContextID contextID,ReplayFunc replayFunc,ReplayWriter & replayWriter,uint32_t frameIndex,std::vector<uint8_t> * binaryData,const std::vector<CallCapture> & calls,std::stringstream & header,std::stringstream & out,size_t * maxResourceIDBufferSize)1630 void WriteCppReplayFunctionWithParts(const gl::ContextID contextID,
1631 ReplayFunc replayFunc,
1632 ReplayWriter &replayWriter,
1633 uint32_t frameIndex,
1634 std::vector<uint8_t> *binaryData,
1635 const std::vector<CallCapture> &calls,
1636 std::stringstream &header,
1637 std::stringstream &out,
1638 size_t *maxResourceIDBufferSize)
1639 {
1640 int callCount = 0;
1641 int partCount = 0;
1642
1643 if (calls.size() > kFunctionSizeLimit)
1644 {
1645 out << "void "
1646 << FmtFunction(replayFunc, contextID, FuncUsage::Definition, frameIndex, ++partCount)
1647 << "\n";
1648 }
1649 else
1650 {
1651 out << "void "
1652 << FmtFunction(replayFunc, contextID, FuncUsage::Definition, frameIndex, kNoPartId)
1653 << "\n";
1654 }
1655
1656 out << "{\n";
1657
1658 for (const CallCapture &call : calls)
1659 {
1660 if (!call.isActive)
1661 {
1662 // Don't write setup calls for inactive resources
1663 continue;
1664 }
1665
1666 out << " ";
1667 WriteCppReplayForCall(call, replayWriter, out, header, binaryData, maxResourceIDBufferSize);
1668 out << ";\n";
1669
1670 if (partCount > 0 && ++callCount % kFunctionSizeLimit == 0)
1671 {
1672 out << "}\n";
1673 out << "\n";
1674 out << "void "
1675 << FmtFunction(replayFunc, contextID, FuncUsage::Definition, frameIndex,
1676 ++partCount)
1677 << "\n";
1678 out << "{\n";
1679 }
1680 }
1681 out << "}\n";
1682
1683 if (partCount > 0)
1684 {
1685 out << "\n";
1686 out << "void "
1687 << FmtFunction(replayFunc, contextID, FuncUsage::Definition, frameIndex, kNoPartId)
1688 << "\n";
1689 out << "{\n";
1690
1691 // Write out the main call which calls all the parts.
1692 for (int i = 1; i <= partCount; i++)
1693 {
1694 out << " " << FmtFunction(replayFunc, contextID, FuncUsage::Call, frameIndex, i)
1695 << ";\n";
1696 }
1697
1698 out << "}\n";
1699 }
1700 }
1701
1702 // Auxiliary contexts are other contexts in the share group that aren't the context calling
1703 // eglSwapBuffers().
WriteAuxiliaryContextCppSetupReplay(ReplayWriter & replayWriter,bool compression,const std::string & outDir,const gl::Context * context,const std::string & captureLabel,uint32_t frameIndex,const std::vector<CallCapture> & setupCalls,std::vector<uint8_t> * binaryData,bool serializeStateEnabled,const FrameCaptureShared & frameCaptureShared,size_t * maxResourceIDBufferSize)1704 void WriteAuxiliaryContextCppSetupReplay(ReplayWriter &replayWriter,
1705 bool compression,
1706 const std::string &outDir,
1707 const gl::Context *context,
1708 const std::string &captureLabel,
1709 uint32_t frameIndex,
1710 const std::vector<CallCapture> &setupCalls,
1711 std::vector<uint8_t> *binaryData,
1712 bool serializeStateEnabled,
1713 const FrameCaptureShared &frameCaptureShared,
1714 size_t *maxResourceIDBufferSize)
1715 {
1716 ASSERT(frameCaptureShared.getWindowSurfaceContextID() != context->id());
1717
1718 {
1719 std::stringstream filenameStream;
1720 filenameStream << outDir << FmtCapturePrefix(context->id(), captureLabel);
1721 std::string filenamePattern = filenameStream.str();
1722 replayWriter.setFilenamePattern(filenamePattern);
1723 }
1724
1725 {
1726 std::stringstream include;
1727 include << "#include \""
1728 << FmtCapturePrefix(frameCaptureShared.getWindowSurfaceContextID(), captureLabel)
1729 << ".h\"\n";
1730 include << "#include \"angle_trace_gl.h\"\n";
1731
1732 std::string frameIncludes = include.str();
1733 replayWriter.setSourcePrologue(frameIncludes);
1734 replayWriter.setHeaderPrologue(frameIncludes);
1735 }
1736
1737 {
1738 std::stringstream protoStream;
1739 std::stringstream headerStream;
1740 std::stringstream bodyStream;
1741
1742 protoStream << "void " << FmtSetupFunction(kNoPartId, context->id(), FuncUsage::Prototype);
1743 std::string proto = protoStream.str();
1744
1745 WriteCppReplayFunctionWithParts(context->id(), ReplayFunc::Setup, replayWriter, frameIndex,
1746 binaryData, setupCalls, headerStream, bodyStream,
1747 maxResourceIDBufferSize);
1748
1749 replayWriter.addPrivateFunction(proto, headerStream, bodyStream);
1750 }
1751
1752 replayWriter.saveFrame();
1753 }
1754
WriteShareGroupCppSetupReplay(ReplayWriter & replayWriter,bool compression,const std::string & outDir,const std::string & captureLabel,uint32_t frameIndex,uint32_t frameCount,const std::vector<CallCapture> & setupCalls,ResourceTracker * resourceTracker,std::vector<uint8_t> * binaryData,bool serializeStateEnabled,gl::ContextID windowSurfaceContextID,size_t * maxResourceIDBufferSize)1755 void WriteShareGroupCppSetupReplay(ReplayWriter &replayWriter,
1756 bool compression,
1757 const std::string &outDir,
1758 const std::string &captureLabel,
1759 uint32_t frameIndex,
1760 uint32_t frameCount,
1761 const std::vector<CallCapture> &setupCalls,
1762 ResourceTracker *resourceTracker,
1763 std::vector<uint8_t> *binaryData,
1764 bool serializeStateEnabled,
1765 gl::ContextID windowSurfaceContextID,
1766 size_t *maxResourceIDBufferSize)
1767 {
1768 {
1769 std::stringstream include;
1770
1771 include << "#include \"angle_trace_gl.h\"\n";
1772 include << "#include \"" << FmtCapturePrefix(windowSurfaceContextID, captureLabel)
1773 << ".h\"\n";
1774
1775 std::string includeString = include.str();
1776
1777 replayWriter.setSourcePrologue(includeString);
1778 }
1779
1780 {
1781 std::stringstream protoStream;
1782 std::stringstream headerStream;
1783 std::stringstream bodyStream;
1784
1785 protoStream << "void "
1786 << FmtSetupFunction(kNoPartId, kSharedContextId, FuncUsage::Prototype);
1787 std::string proto = protoStream.str();
1788
1789 WriteCppReplayFunctionWithParts(kSharedContextId, ReplayFunc::Setup, replayWriter,
1790 frameIndex, binaryData, setupCalls, headerStream,
1791 bodyStream, maxResourceIDBufferSize);
1792
1793 replayWriter.addPrivateFunction(proto, headerStream, bodyStream);
1794 }
1795
1796 {
1797 std::stringstream filenameStream;
1798 filenameStream << outDir << FmtCapturePrefix(kSharedContextId, captureLabel);
1799
1800 std::string filenamePattern = filenameStream.str();
1801
1802 replayWriter.setFilenamePattern(filenamePattern);
1803 }
1804
1805 replayWriter.saveSetupFile();
1806 }
1807
GetAttachedProgramSources(const gl::Program * program)1808 ProgramSources GetAttachedProgramSources(const gl::Program *program)
1809 {
1810 ProgramSources sources;
1811 for (gl::ShaderType shaderType : gl::AllShaderTypes())
1812 {
1813 const gl::Shader *shader = program->getAttachedShader(shaderType);
1814 if (shader)
1815 {
1816 sources[shaderType] = shader->getSourceString();
1817 }
1818 }
1819 return sources;
1820 }
1821
1822 template <typename IDType>
CaptureUpdateResourceIDs(const gl::Context * context,const CallCapture & call,const ParamCapture & param,ResourceTracker * resourceTracker,std::vector<CallCapture> * callsOut)1823 void CaptureUpdateResourceIDs(const gl::Context *context,
1824 const CallCapture &call,
1825 const ParamCapture ¶m,
1826 ResourceTracker *resourceTracker,
1827 std::vector<CallCapture> *callsOut)
1828 {
1829 GLsizei n = call.params.getParamFlexName("n", "count", ParamType::TGLsizei, 0).value.GLsizeiVal;
1830 ASSERT(param.data.size() == 1);
1831 ResourceIDType resourceIDType = GetResourceIDTypeFromParamType(param.type);
1832 ASSERT(resourceIDType != ResourceIDType::InvalidEnum &&
1833 resourceIDType != ResourceIDType::ShaderProgram);
1834 const char *resourceName = GetResourceIDTypeName(resourceIDType);
1835
1836 std::stringstream updateFuncNameStr;
1837 updateFuncNameStr << "Update" << resourceName << "ID";
1838 std::string updateFuncName = updateFuncNameStr.str();
1839
1840 const IDType *returnedIDs = reinterpret_cast<const IDType *>(param.data[0].data());
1841
1842 ResourceSet &startingSet =
1843 resourceTracker->getTrackedResource(context->id(), resourceIDType).getStartingResources();
1844
1845 for (GLsizei idIndex = 0; idIndex < n; ++idIndex)
1846 {
1847 IDType id = returnedIDs[idIndex];
1848 GLsizei readBufferOffset = idIndex * sizeof(gl::RenderbufferID);
1849 ParamBuffer params;
1850 params.addValueParam("id", ParamType::TGLuint, id.value);
1851 params.addValueParam("readBufferOffset", ParamType::TGLsizei, readBufferOffset);
1852 callsOut->emplace_back(updateFuncName, std::move(params));
1853
1854 // Add only if not in starting resources.
1855 if (startingSet.find(id.value) == startingSet.end())
1856 {
1857 resourceTracker->getTrackedResource(context->id(), resourceIDType)
1858 .getNewResources()
1859 .insert(id.value);
1860 }
1861 }
1862 }
1863
CaptureUpdateUniformLocations(const gl::Program * program,std::vector<CallCapture> * callsOut)1864 void CaptureUpdateUniformLocations(const gl::Program *program, std::vector<CallCapture> *callsOut)
1865 {
1866 const std::vector<gl::LinkedUniform> &uniforms = program->getState().getUniforms();
1867 const std::vector<gl::VariableLocation> &locations = program->getUniformLocations();
1868
1869 for (GLint location = 0; location < static_cast<GLint>(locations.size()); ++location)
1870 {
1871 const gl::VariableLocation &locationVar = locations[location];
1872
1873 // This handles the case where the application calls glBindUniformLocationCHROMIUM
1874 // on an unused uniform. We must still store a -1 into gUniformLocations in case the
1875 // application attempts to call a glUniform* call. To do this we'll pass in a blank name to
1876 // force glGetUniformLocation to return -1.
1877 std::string name;
1878 int count = 1;
1879 ParamBuffer params;
1880 params.addValueParam("program", ParamType::TGLuint, program->id().value);
1881
1882 if (locationVar.index >= uniforms.size())
1883 {
1884 name = "";
1885 }
1886 else
1887 {
1888 const gl::LinkedUniform &uniform = uniforms[locationVar.index];
1889
1890 name = uniform.name;
1891
1892 if (uniform.isArray())
1893 {
1894 if (locationVar.arrayIndex > 0)
1895 {
1896 // Non-sequential array uniform locations are not currently handled.
1897 // In practice array locations shouldn't ever be non-sequential.
1898 ASSERT(uniform.getLocation() == -1 ||
1899 location ==
1900 uniform.getLocation() + static_cast<int>(locationVar.arrayIndex));
1901 continue;
1902 }
1903
1904 if (uniform.isArrayOfArrays())
1905 {
1906 UNIMPLEMENTED();
1907 }
1908
1909 name = gl::StripLastArrayIndex(name);
1910 count = uniform.arraySizes[0];
1911 }
1912 }
1913
1914 ParamCapture nameParam("name", ParamType::TGLcharConstPointer);
1915 CaptureString(name.c_str(), &nameParam);
1916 params.addParam(std::move(nameParam));
1917 params.addValueParam("location", ParamType::TGLint, location);
1918 params.addValueParam("count", ParamType::TGLint, static_cast<GLint>(count));
1919 callsOut->emplace_back("UpdateUniformLocation", std::move(params));
1920 }
1921 }
1922
CaptureValidateSerializedState(const gl::Context * context,std::vector<CallCapture> * callsOut)1923 void CaptureValidateSerializedState(const gl::Context *context, std::vector<CallCapture> *callsOut)
1924 {
1925 INFO() << "Capturing validation checkpoint at position " << callsOut->size();
1926
1927 context->finishImmutable();
1928
1929 std::string serializedState;
1930 angle::Result result = angle::SerializeContextToString(context, &serializedState);
1931 if (result != angle::Result::Continue)
1932 {
1933 ERR() << "Internal error serializing context state.";
1934 return;
1935 }
1936 ParamCapture serializedStateParam("serializedState", ParamType::TGLcharConstPointer);
1937 CaptureString(serializedState.c_str(), &serializedStateParam);
1938
1939 ParamBuffer params;
1940 params.addParam(std::move(serializedStateParam));
1941
1942 callsOut->emplace_back("VALIDATE_CHECKPOINT", std::move(params));
1943 }
1944
CaptureUpdateUniformBlockIndexes(const gl::Program * program,std::vector<CallCapture> * callsOut)1945 void CaptureUpdateUniformBlockIndexes(const gl::Program *program,
1946 std::vector<CallCapture> *callsOut)
1947 {
1948 const std::vector<gl::InterfaceBlock> &uniformBlocks = program->getState().getUniformBlocks();
1949
1950 for (GLuint index = 0; index < uniformBlocks.size(); ++index)
1951 {
1952 ParamBuffer params;
1953
1954 std::string name;
1955 params.addValueParam("program", ParamType::TShaderProgramID, program->id());
1956
1957 const std::string fullName = uniformBlocks[index].nameWithArrayIndex();
1958 ParamCapture nameParam("name", ParamType::TGLcharConstPointer);
1959 CaptureString(fullName.c_str(), &nameParam);
1960 params.addParam(std::move(nameParam));
1961
1962 params.addValueParam("index", ParamType::TGLuint, index);
1963 callsOut->emplace_back("UpdateUniformBlockIndex", std::move(params));
1964 }
1965 }
1966
CaptureDeleteUniformLocations(gl::ShaderProgramID program,std::vector<CallCapture> * callsOut)1967 void CaptureDeleteUniformLocations(gl::ShaderProgramID program, std::vector<CallCapture> *callsOut)
1968 {
1969 ParamBuffer params;
1970 params.addValueParam("program", ParamType::TShaderProgramID, program);
1971 callsOut->emplace_back("DeleteUniformLocations", std::move(params));
1972 }
1973
MaybeCaptureUpdateResourceIDs(const gl::Context * context,ResourceTracker * resourceTracker,std::vector<CallCapture> * callsOut)1974 void MaybeCaptureUpdateResourceIDs(const gl::Context *context,
1975 ResourceTracker *resourceTracker,
1976 std::vector<CallCapture> *callsOut)
1977 {
1978 const CallCapture &call = callsOut->back();
1979
1980 switch (call.entryPoint)
1981 {
1982 case EntryPoint::GLGenBuffers:
1983 {
1984 const ParamCapture &buffers =
1985 call.params.getParam("buffersPacked", ParamType::TBufferIDPointer, 1);
1986 CaptureUpdateResourceIDs<gl::BufferID>(context, call, buffers, resourceTracker,
1987 callsOut);
1988 break;
1989 }
1990
1991 case EntryPoint::GLGenFencesNV:
1992 {
1993 const ParamCapture &fences =
1994 call.params.getParam("fencesPacked", ParamType::TFenceNVIDPointer, 1);
1995 CaptureUpdateResourceIDs<gl::FenceNVID>(context, call, fences, resourceTracker,
1996 callsOut);
1997 break;
1998 }
1999
2000 case EntryPoint::GLGenFramebuffers:
2001 case EntryPoint::GLGenFramebuffersOES:
2002 {
2003 const ParamCapture &framebuffers =
2004 call.params.getParam("framebuffersPacked", ParamType::TFramebufferIDPointer, 1);
2005 CaptureUpdateResourceIDs<gl::FramebufferID>(context, call, framebuffers,
2006 resourceTracker, callsOut);
2007 break;
2008 }
2009
2010 case EntryPoint::GLGenProgramPipelines:
2011 {
2012 const ParamCapture &pipelines =
2013 call.params.getParam("pipelinesPacked", ParamType::TProgramPipelineIDPointer, 1);
2014 CaptureUpdateResourceIDs<gl::ProgramPipelineID>(context, call, pipelines,
2015 resourceTracker, callsOut);
2016 break;
2017 }
2018
2019 case EntryPoint::GLGenQueries:
2020 case EntryPoint::GLGenQueriesEXT:
2021 {
2022 const ParamCapture &queries =
2023 call.params.getParam("idsPacked", ParamType::TQueryIDPointer, 1);
2024 CaptureUpdateResourceIDs<gl::QueryID>(context, call, queries, resourceTracker,
2025 callsOut);
2026 break;
2027 }
2028
2029 case EntryPoint::GLGenRenderbuffers:
2030 case EntryPoint::GLGenRenderbuffersOES:
2031 {
2032 const ParamCapture &renderbuffers =
2033 call.params.getParam("renderbuffersPacked", ParamType::TRenderbufferIDPointer, 1);
2034 CaptureUpdateResourceIDs<gl::RenderbufferID>(context, call, renderbuffers,
2035 resourceTracker, callsOut);
2036 break;
2037 }
2038
2039 case EntryPoint::GLGenSamplers:
2040 {
2041 const ParamCapture &samplers =
2042 call.params.getParam("samplersPacked", ParamType::TSamplerIDPointer, 1);
2043 CaptureUpdateResourceIDs<gl::SamplerID>(context, call, samplers, resourceTracker,
2044 callsOut);
2045 break;
2046 }
2047
2048 case EntryPoint::GLGenSemaphoresEXT:
2049 {
2050 const ParamCapture &semaphores =
2051 call.params.getParam("semaphoresPacked", ParamType::TSemaphoreIDPointer, 1);
2052 CaptureUpdateResourceIDs<gl::SemaphoreID>(context, call, semaphores, resourceTracker,
2053 callsOut);
2054 break;
2055 }
2056
2057 case EntryPoint::GLGenTextures:
2058 {
2059 const ParamCapture &textures =
2060 call.params.getParam("texturesPacked", ParamType::TTextureIDPointer, 1);
2061 CaptureUpdateResourceIDs<gl::TextureID>(context, call, textures, resourceTracker,
2062 callsOut);
2063 break;
2064 }
2065
2066 case EntryPoint::GLGenTransformFeedbacks:
2067 {
2068 const ParamCapture &xfbs =
2069 call.params.getParam("idsPacked", ParamType::TTransformFeedbackIDPointer, 1);
2070 CaptureUpdateResourceIDs<gl::TransformFeedbackID>(context, call, xfbs, resourceTracker,
2071 callsOut);
2072 break;
2073 }
2074
2075 case EntryPoint::GLGenVertexArrays:
2076 case EntryPoint::GLGenVertexArraysOES:
2077 {
2078 const ParamCapture &vertexArrays =
2079 call.params.getParam("arraysPacked", ParamType::TVertexArrayIDPointer, 1);
2080 CaptureUpdateResourceIDs<gl::VertexArrayID>(context, call, vertexArrays,
2081 resourceTracker, callsOut);
2082 break;
2083 }
2084
2085 case EntryPoint::GLCreateMemoryObjectsEXT:
2086 {
2087 const ParamCapture &memoryObjects =
2088 call.params.getParam("memoryObjectsPacked", ParamType::TMemoryObjectIDPointer, 1);
2089 CaptureUpdateResourceIDs<gl::MemoryObjectID>(context, call, memoryObjects,
2090 resourceTracker, callsOut);
2091 break;
2092 }
2093
2094 default:
2095 break;
2096 }
2097 }
2098
IsDefaultCurrentValue(const gl::VertexAttribCurrentValueData & currentValue)2099 bool IsDefaultCurrentValue(const gl::VertexAttribCurrentValueData ¤tValue)
2100 {
2101 if (currentValue.Type != gl::VertexAttribType::Float)
2102 return false;
2103
2104 return currentValue.Values.FloatValues[0] == 0.0f &&
2105 currentValue.Values.FloatValues[1] == 0.0f &&
2106 currentValue.Values.FloatValues[2] == 0.0f && currentValue.Values.FloatValues[3] == 1.0f;
2107 }
2108
IsQueryActive(const gl::State & glState,gl::QueryID & queryID)2109 bool IsQueryActive(const gl::State &glState, gl::QueryID &queryID)
2110 {
2111 const gl::ActiveQueryMap &activeQueries = glState.getActiveQueriesForCapture();
2112 for (const auto &activeQueryIter : activeQueries)
2113 {
2114 const gl::Query *activeQuery = activeQueryIter.get();
2115 if (activeQuery && activeQuery->id() == queryID)
2116 {
2117 return true;
2118 }
2119 }
2120
2121 return false;
2122 }
2123
IsTextureUpdate(CallCapture & call)2124 bool IsTextureUpdate(CallCapture &call)
2125 {
2126 switch (call.entryPoint)
2127 {
2128 case EntryPoint::GLCompressedCopyTextureCHROMIUM:
2129 case EntryPoint::GLCompressedTexImage1D:
2130 case EntryPoint::GLCompressedTexImage2D:
2131 case EntryPoint::GLCompressedTexImage2DRobustANGLE:
2132 case EntryPoint::GLCompressedTexImage3D:
2133 case EntryPoint::GLCompressedTexImage3DOES:
2134 case EntryPoint::GLCompressedTexImage3DRobustANGLE:
2135 case EntryPoint::GLCompressedTexSubImage1D:
2136 case EntryPoint::GLCompressedTexSubImage2D:
2137 case EntryPoint::GLCompressedTexSubImage2DRobustANGLE:
2138 case EntryPoint::GLCompressedTexSubImage3D:
2139 case EntryPoint::GLCompressedTexSubImage3DOES:
2140 case EntryPoint::GLCompressedTexSubImage3DRobustANGLE:
2141 case EntryPoint::GLCompressedTextureSubImage1D:
2142 case EntryPoint::GLCompressedTextureSubImage2D:
2143 case EntryPoint::GLCompressedTextureSubImage3D:
2144 case EntryPoint::GLCopyTexImage1D:
2145 case EntryPoint::GLCopyTexImage2D:
2146 case EntryPoint::GLCopyTexSubImage1D:
2147 case EntryPoint::GLCopyTexSubImage2D:
2148 case EntryPoint::GLCopyTexSubImage3D:
2149 case EntryPoint::GLCopyTexSubImage3DOES:
2150 case EntryPoint::GLCopyTexture3DANGLE:
2151 case EntryPoint::GLCopyTextureCHROMIUM:
2152 case EntryPoint::GLCopyTextureSubImage1D:
2153 case EntryPoint::GLCopyTextureSubImage2D:
2154 case EntryPoint::GLCopyTextureSubImage3D:
2155 case EntryPoint::GLTexImage1D:
2156 case EntryPoint::GLTexImage2D:
2157 case EntryPoint::GLTexImage2DExternalANGLE:
2158 case EntryPoint::GLTexImage2DMultisample:
2159 case EntryPoint::GLTexImage2DRobustANGLE:
2160 case EntryPoint::GLTexImage3D:
2161 case EntryPoint::GLTexImage3DMultisample:
2162 case EntryPoint::GLTexImage3DOES:
2163 case EntryPoint::GLTexImage3DRobustANGLE:
2164 case EntryPoint::GLTexSubImage1D:
2165 case EntryPoint::GLTexSubImage2D:
2166 case EntryPoint::GLTexSubImage2DRobustANGLE:
2167 case EntryPoint::GLTexSubImage3D:
2168 case EntryPoint::GLTexSubImage3DOES:
2169 case EntryPoint::GLTexSubImage3DRobustANGLE:
2170 case EntryPoint::GLTextureSubImage1D:
2171 case EntryPoint::GLTextureSubImage2D:
2172 case EntryPoint::GLTextureSubImage3D:
2173 case EntryPoint::GLCopyImageSubData:
2174 case EntryPoint::GLCopyImageSubDataEXT:
2175 case EntryPoint::GLCopyImageSubDataOES:
2176 return true;
2177 default:
2178 return false;
2179 }
2180 }
2181
IsVertexArrayUpdate(CallCapture & call)2182 bool IsVertexArrayUpdate(CallCapture &call)
2183 {
2184 switch (call.entryPoint)
2185 {
2186 case EntryPoint::GLVertexAttribFormat:
2187 case EntryPoint::GLVertexAttribIFormat:
2188 case EntryPoint::GLBindVertexBuffer:
2189 case EntryPoint::GLVertexAttribBinding:
2190 case EntryPoint::GLVertexAttribPointer:
2191 case EntryPoint::GLVertexAttribIPointer:
2192 case EntryPoint::GLEnableVertexAttribArray:
2193 case EntryPoint::GLDisableVertexAttribArray:
2194 case EntryPoint::GLVertexBindingDivisor:
2195 case EntryPoint::GLVertexAttribDivisor:
2196 return true;
2197 default:
2198 return false;
2199 }
2200 }
2201
IsSharedObjectResource(ResourceIDType type)2202 bool IsSharedObjectResource(ResourceIDType type)
2203 {
2204 // This helper function informs us which objects are shared vs. per context
2205 //
2206 // OpenGL ES Version 3.2 (October 22, 2019)
2207 // Chapter 5 Shared Objects and Multiple Contexts:
2208 //
2209 // - Objects that can be shared between contexts include buffer objects, program
2210 // and shader objects, renderbuffer objects, sampler objects, sync objects, and texture
2211 // objects (except for the texture objects named zero).
2212 // - Objects which contain references to other objects include framebuffer, program
2213 // pipeline, transform feedback, and vertex array objects. Such objects are called
2214 // container objects and are not shared.
2215 //
2216 // Notably absent from this list are Sync objects, which are not ResourceIDType, are handled
2217 // elsewhere, and are shared:
2218 // - 2.6.13 Sync Objects: Sync objects may be shared.
2219
2220 switch (type)
2221 {
2222 case ResourceIDType::Buffer:
2223 // 2.6.2 Buffer Objects: Buffer objects may be shared.
2224 return true;
2225
2226 case ResourceIDType::Framebuffer:
2227 // 2.6.9 Framebuffer Objects: Framebuffer objects are container objects including
2228 // references to renderbuffer and / or texture objects, and are not shared.
2229 return false;
2230
2231 case ResourceIDType::ProgramPipeline:
2232 // 2.6.5 Program Pipeline Objects: Program pipeline objects are container objects
2233 // including references to program objects, and are not shared.
2234 return false;
2235
2236 case ResourceIDType::TransformFeedback:
2237 // 2.6.11 Transform Feedback Objects: Transform feedback objects are container objects
2238 // including references to buffer objects, and are not shared
2239 return false;
2240
2241 case ResourceIDType::VertexArray:
2242 // 2.6.10 Vertex Array Objects: Vertex array objects are container objects including
2243 // references to buffer objects, and are not shared
2244 return false;
2245
2246 case ResourceIDType::FenceNV:
2247 // From https://registry.khronos.org/OpenGL/extensions/NV/NV_fence.txt
2248 // Are the fences sharable between multiple contexts?
2249 // RESOLUTION: No.
2250 return false;
2251
2252 case ResourceIDType::Renderbuffer:
2253 // 2.6.8 Renderbuffer Objects: Renderbuffer objects may be shared.
2254 return true;
2255
2256 case ResourceIDType::ShaderProgram:
2257 // 2.6.3 Shader Objects: Shader objects may be shared.
2258 // 2.6.4 Program Objects: Program objects may be shared.
2259 return true;
2260
2261 case ResourceIDType::Sampler:
2262 // 2.6.7 Sampler Objects: Sampler objects may be shared
2263 return true;
2264
2265 case ResourceIDType::Sync:
2266 // 2.6.13 Sync Objects: Sync objects may be shared.
2267 return true;
2268
2269 case ResourceIDType::Texture:
2270 // 2.6.6 Texture Objects: Texture objects may be shared
2271 return true;
2272
2273 case ResourceIDType::Query:
2274 // 2.6.12 Query Objects: Query objects are not shared
2275 return false;
2276
2277 case ResourceIDType::Semaphore:
2278 // From https://registry.khronos.org/OpenGL/extensions/EXT/EXT_external_objects.txt
2279 // 2.6.14 Semaphore Objects: Semaphore objects may be shared.
2280 return true;
2281
2282 case ResourceIDType::MemoryObject:
2283 // From https://registry.khronos.org/OpenGL/extensions/EXT/EXT_external_objects.txt
2284 // 2.6.15 Memory Objects: Memory objects may be shared.
2285 return true;
2286
2287 case ResourceIDType::Context:
2288 case ResourceIDType::Image:
2289 case ResourceIDType::Surface:
2290 case ResourceIDType::egl_Sync:
2291 // EGL types are associated with a display and not bound to a context
2292 // For the way this function is used, we can treat them as shared.
2293 return true;
2294
2295 case ResourceIDType::EnumCount:
2296 default:
2297 ERR() << "Unhandled ResourceIDType= " << static_cast<int>(type);
2298 UNREACHABLE();
2299 return false;
2300 }
2301 }
2302
2303 enum class DefaultUniformType
2304 {
2305 None,
2306 CurrentProgram,
2307 SpecifiedProgram,
2308 };
2309
GetDefaultUniformType(const CallCapture & call)2310 DefaultUniformType GetDefaultUniformType(const CallCapture &call)
2311 {
2312 switch (call.entryPoint)
2313 {
2314 case EntryPoint::GLProgramUniform1d:
2315 case EntryPoint::GLProgramUniform1dv:
2316 case EntryPoint::GLProgramUniform1f:
2317 case EntryPoint::GLProgramUniform1fEXT:
2318 case EntryPoint::GLProgramUniform1fv:
2319 case EntryPoint::GLProgramUniform1fvEXT:
2320 case EntryPoint::GLProgramUniform1i:
2321 case EntryPoint::GLProgramUniform1iEXT:
2322 case EntryPoint::GLProgramUniform1iv:
2323 case EntryPoint::GLProgramUniform1ivEXT:
2324 case EntryPoint::GLProgramUniform1ui:
2325 case EntryPoint::GLProgramUniform1uiEXT:
2326 case EntryPoint::GLProgramUniform1uiv:
2327 case EntryPoint::GLProgramUniform1uivEXT:
2328 case EntryPoint::GLProgramUniform2d:
2329 case EntryPoint::GLProgramUniform2dv:
2330 case EntryPoint::GLProgramUniform2f:
2331 case EntryPoint::GLProgramUniform2fEXT:
2332 case EntryPoint::GLProgramUniform2fv:
2333 case EntryPoint::GLProgramUniform2fvEXT:
2334 case EntryPoint::GLProgramUniform2i:
2335 case EntryPoint::GLProgramUniform2iEXT:
2336 case EntryPoint::GLProgramUniform2iv:
2337 case EntryPoint::GLProgramUniform2ivEXT:
2338 case EntryPoint::GLProgramUniform2ui:
2339 case EntryPoint::GLProgramUniform2uiEXT:
2340 case EntryPoint::GLProgramUniform2uiv:
2341 case EntryPoint::GLProgramUniform2uivEXT:
2342 case EntryPoint::GLProgramUniform3d:
2343 case EntryPoint::GLProgramUniform3dv:
2344 case EntryPoint::GLProgramUniform3f:
2345 case EntryPoint::GLProgramUniform3fEXT:
2346 case EntryPoint::GLProgramUniform3fv:
2347 case EntryPoint::GLProgramUniform3fvEXT:
2348 case EntryPoint::GLProgramUniform3i:
2349 case EntryPoint::GLProgramUniform3iEXT:
2350 case EntryPoint::GLProgramUniform3iv:
2351 case EntryPoint::GLProgramUniform3ivEXT:
2352 case EntryPoint::GLProgramUniform3ui:
2353 case EntryPoint::GLProgramUniform3uiEXT:
2354 case EntryPoint::GLProgramUniform3uiv:
2355 case EntryPoint::GLProgramUniform3uivEXT:
2356 case EntryPoint::GLProgramUniform4d:
2357 case EntryPoint::GLProgramUniform4dv:
2358 case EntryPoint::GLProgramUniform4f:
2359 case EntryPoint::GLProgramUniform4fEXT:
2360 case EntryPoint::GLProgramUniform4fv:
2361 case EntryPoint::GLProgramUniform4fvEXT:
2362 case EntryPoint::GLProgramUniform4i:
2363 case EntryPoint::GLProgramUniform4iEXT:
2364 case EntryPoint::GLProgramUniform4iv:
2365 case EntryPoint::GLProgramUniform4ivEXT:
2366 case EntryPoint::GLProgramUniform4ui:
2367 case EntryPoint::GLProgramUniform4uiEXT:
2368 case EntryPoint::GLProgramUniform4uiv:
2369 case EntryPoint::GLProgramUniform4uivEXT:
2370 case EntryPoint::GLProgramUniformMatrix2dv:
2371 case EntryPoint::GLProgramUniformMatrix2fv:
2372 case EntryPoint::GLProgramUniformMatrix2fvEXT:
2373 case EntryPoint::GLProgramUniformMatrix2x3dv:
2374 case EntryPoint::GLProgramUniformMatrix2x3fv:
2375 case EntryPoint::GLProgramUniformMatrix2x3fvEXT:
2376 case EntryPoint::GLProgramUniformMatrix2x4dv:
2377 case EntryPoint::GLProgramUniformMatrix2x4fv:
2378 case EntryPoint::GLProgramUniformMatrix2x4fvEXT:
2379 case EntryPoint::GLProgramUniformMatrix3dv:
2380 case EntryPoint::GLProgramUniformMatrix3fv:
2381 case EntryPoint::GLProgramUniformMatrix3fvEXT:
2382 case EntryPoint::GLProgramUniformMatrix3x2dv:
2383 case EntryPoint::GLProgramUniformMatrix3x2fv:
2384 case EntryPoint::GLProgramUniformMatrix3x2fvEXT:
2385 case EntryPoint::GLProgramUniformMatrix3x4dv:
2386 case EntryPoint::GLProgramUniformMatrix3x4fv:
2387 case EntryPoint::GLProgramUniformMatrix3x4fvEXT:
2388 case EntryPoint::GLProgramUniformMatrix4dv:
2389 case EntryPoint::GLProgramUniformMatrix4fv:
2390 case EntryPoint::GLProgramUniformMatrix4fvEXT:
2391 case EntryPoint::GLProgramUniformMatrix4x2dv:
2392 case EntryPoint::GLProgramUniformMatrix4x2fv:
2393 case EntryPoint::GLProgramUniformMatrix4x2fvEXT:
2394 case EntryPoint::GLProgramUniformMatrix4x3dv:
2395 case EntryPoint::GLProgramUniformMatrix4x3fv:
2396 case EntryPoint::GLProgramUniformMatrix4x3fvEXT:
2397 return DefaultUniformType::SpecifiedProgram;
2398
2399 case EntryPoint::GLUniform1d:
2400 case EntryPoint::GLUniform1dv:
2401 case EntryPoint::GLUniform1f:
2402 case EntryPoint::GLUniform1fv:
2403 case EntryPoint::GLUniform1i:
2404 case EntryPoint::GLUniform1iv:
2405 case EntryPoint::GLUniform1ui:
2406 case EntryPoint::GLUniform1uiv:
2407 case EntryPoint::GLUniform2d:
2408 case EntryPoint::GLUniform2dv:
2409 case EntryPoint::GLUniform2f:
2410 case EntryPoint::GLUniform2fv:
2411 case EntryPoint::GLUniform2i:
2412 case EntryPoint::GLUniform2iv:
2413 case EntryPoint::GLUniform2ui:
2414 case EntryPoint::GLUniform2uiv:
2415 case EntryPoint::GLUniform3d:
2416 case EntryPoint::GLUniform3dv:
2417 case EntryPoint::GLUniform3f:
2418 case EntryPoint::GLUniform3fv:
2419 case EntryPoint::GLUniform3i:
2420 case EntryPoint::GLUniform3iv:
2421 case EntryPoint::GLUniform3ui:
2422 case EntryPoint::GLUniform3uiv:
2423 case EntryPoint::GLUniform4d:
2424 case EntryPoint::GLUniform4dv:
2425 case EntryPoint::GLUniform4f:
2426 case EntryPoint::GLUniform4fv:
2427 case EntryPoint::GLUniform4i:
2428 case EntryPoint::GLUniform4iv:
2429 case EntryPoint::GLUniform4ui:
2430 case EntryPoint::GLUniform4uiv:
2431 case EntryPoint::GLUniformMatrix2dv:
2432 case EntryPoint::GLUniformMatrix2fv:
2433 case EntryPoint::GLUniformMatrix2x3dv:
2434 case EntryPoint::GLUniformMatrix2x3fv:
2435 case EntryPoint::GLUniformMatrix2x4dv:
2436 case EntryPoint::GLUniformMatrix2x4fv:
2437 case EntryPoint::GLUniformMatrix3dv:
2438 case EntryPoint::GLUniformMatrix3fv:
2439 case EntryPoint::GLUniformMatrix3x2dv:
2440 case EntryPoint::GLUniformMatrix3x2fv:
2441 case EntryPoint::GLUniformMatrix3x4dv:
2442 case EntryPoint::GLUniformMatrix3x4fv:
2443 case EntryPoint::GLUniformMatrix4dv:
2444 case EntryPoint::GLUniformMatrix4fv:
2445 case EntryPoint::GLUniformMatrix4x2dv:
2446 case EntryPoint::GLUniformMatrix4x2fv:
2447 case EntryPoint::GLUniformMatrix4x3dv:
2448 case EntryPoint::GLUniformMatrix4x3fv:
2449 case EntryPoint::GLUniformSubroutinesuiv:
2450 return DefaultUniformType::CurrentProgram;
2451
2452 default:
2453 return DefaultUniformType::None;
2454 }
2455 }
2456
CaptureFramebufferAttachment(std::vector<CallCapture> * setupCalls,const gl::State & replayState,const FramebufferCaptureFuncs & framebufferFuncs,const gl::FramebufferAttachment & attachment)2457 void CaptureFramebufferAttachment(std::vector<CallCapture> *setupCalls,
2458 const gl::State &replayState,
2459 const FramebufferCaptureFuncs &framebufferFuncs,
2460 const gl::FramebufferAttachment &attachment)
2461 {
2462 GLuint resourceID = attachment.getResource()->getId();
2463
2464 if (attachment.type() == GL_TEXTURE)
2465 {
2466 gl::ImageIndex index = attachment.getTextureImageIndex();
2467
2468 if (index.usesTex3D())
2469 {
2470 Capture(setupCalls, CaptureFramebufferTextureLayer(
2471 replayState, true, GL_FRAMEBUFFER, attachment.getBinding(),
2472 {resourceID}, index.getLevelIndex(), index.getLayerIndex()));
2473 }
2474 else
2475 {
2476 Capture(setupCalls,
2477 framebufferFuncs.framebufferTexture2D(
2478 replayState, true, GL_FRAMEBUFFER, attachment.getBinding(),
2479 index.getTargetOrFirstCubeFace(), {resourceID}, index.getLevelIndex()));
2480 }
2481 }
2482 else
2483 {
2484 ASSERT(attachment.type() == GL_RENDERBUFFER);
2485 Capture(setupCalls, framebufferFuncs.framebufferRenderbuffer(
2486 replayState, true, GL_FRAMEBUFFER, attachment.getBinding(),
2487 GL_RENDERBUFFER, {resourceID}));
2488 }
2489 }
2490
CaptureUpdateUniformValues(const gl::State & replayState,const gl::Context * context,gl::Program * program,ResourceTracker * resourceTracker,std::vector<CallCapture> * callsOut)2491 void CaptureUpdateUniformValues(const gl::State &replayState,
2492 const gl::Context *context,
2493 gl::Program *program,
2494 ResourceTracker *resourceTracker,
2495 std::vector<CallCapture> *callsOut)
2496 {
2497 if (!program->isLinked())
2498 {
2499 // We can't populate uniforms if the program hasn't been linked
2500 return;
2501 }
2502
2503 // We need to bind the program and update its uniforms
2504 if (!replayState.getProgram() || replayState.getProgram()->id() != program->id())
2505 {
2506 Capture(callsOut, CaptureUseProgram(replayState, true, program->id()));
2507 CaptureUpdateCurrentProgram(callsOut->back(), 0, callsOut);
2508 }
2509
2510 const std::vector<gl::LinkedUniform> &uniforms = program->getState().getUniforms();
2511
2512 for (const gl::LinkedUniform &uniform : uniforms)
2513 {
2514 std::string uniformName = uniform.name;
2515
2516 int uniformCount = 1;
2517 if (uniform.isArray())
2518 {
2519 if (uniform.isArrayOfArrays())
2520 {
2521 UNIMPLEMENTED();
2522 continue;
2523 }
2524
2525 uniformCount = uniform.arraySizes[0];
2526 uniformName = gl::StripLastArrayIndex(uniformName);
2527 }
2528
2529 gl::UniformLocation uniformLoc = program->getUniformLocation(uniformName);
2530 const gl::UniformTypeInfo *typeInfo = uniform.typeInfo;
2531 int componentCount = typeInfo->componentCount;
2532 int uniformSize = uniformCount * componentCount;
2533
2534 // For arrayed uniforms, we'll need to increment a read location
2535 gl::UniformLocation readLoc = uniformLoc;
2536
2537 // If the uniform is unused, just continue
2538 if (readLoc.value == -1)
2539 {
2540 continue;
2541 }
2542
2543 // Image uniforms are special and cannot be set this way
2544 if (typeInfo->isImageType)
2545 {
2546 continue;
2547 }
2548
2549 DefaultUniformCallsPerLocationMap &resetCalls =
2550 resourceTracker->getDefaultUniformResetCalls(program->id());
2551
2552 // Create two lists of calls for uniforms, one for Setup, one for Reset
2553 CallVector defaultUniformCalls({callsOut, &resetCalls[uniformLoc]});
2554
2555 // Samplers should be populated with GL_INT, regardless of return type
2556 if (typeInfo->isSampler)
2557 {
2558 std::vector<GLint> uniformBuffer(uniformSize);
2559 for (int index = 0; index < uniformCount; index++, readLoc.value++)
2560 {
2561 program->getUniformiv(context, readLoc,
2562 uniformBuffer.data() + index * componentCount);
2563 resourceTracker->setDefaultUniformBaseLocation(program->id(), readLoc, uniformLoc);
2564 }
2565
2566 for (std::vector<CallCapture> *calls : defaultUniformCalls)
2567 {
2568 Capture(calls, CaptureUniform1iv(replayState, true, uniformLoc, uniformCount,
2569 uniformBuffer.data()));
2570 }
2571
2572 continue;
2573 }
2574
2575 switch (typeInfo->componentType)
2576 {
2577 case GL_FLOAT:
2578 {
2579 std::vector<GLfloat> uniformBuffer(uniformSize);
2580 for (int index = 0; index < uniformCount; index++, readLoc.value++)
2581 {
2582 program->getUniformfv(context, readLoc,
2583 uniformBuffer.data() + index * componentCount);
2584 resourceTracker->setDefaultUniformBaseLocation(program->id(), readLoc,
2585 uniformLoc);
2586 }
2587 switch (typeInfo->type)
2588 {
2589 // Note: All matrix uniforms are populated without transpose
2590 case GL_FLOAT_MAT4x3:
2591 for (std::vector<CallCapture> *calls : defaultUniformCalls)
2592 {
2593 Capture(calls, CaptureUniformMatrix4x3fv(replayState, true, uniformLoc,
2594 uniformCount, false,
2595 uniformBuffer.data()));
2596 }
2597 break;
2598 case GL_FLOAT_MAT4x2:
2599 for (std::vector<CallCapture> *calls : defaultUniformCalls)
2600 {
2601 Capture(calls, CaptureUniformMatrix4x2fv(replayState, true, uniformLoc,
2602 uniformCount, false,
2603 uniformBuffer.data()));
2604 }
2605 break;
2606 case GL_FLOAT_MAT4:
2607 for (std::vector<CallCapture> *calls : defaultUniformCalls)
2608 {
2609 Capture(calls, CaptureUniformMatrix4fv(replayState, true, uniformLoc,
2610 uniformCount, false,
2611 uniformBuffer.data()));
2612 }
2613 break;
2614 case GL_FLOAT_MAT3x4:
2615 for (std::vector<CallCapture> *calls : defaultUniformCalls)
2616 {
2617 Capture(calls, CaptureUniformMatrix3x4fv(replayState, true, uniformLoc,
2618 uniformCount, false,
2619 uniformBuffer.data()));
2620 }
2621 break;
2622 case GL_FLOAT_MAT3x2:
2623 for (std::vector<CallCapture> *calls : defaultUniformCalls)
2624 {
2625 Capture(calls, CaptureUniformMatrix3x2fv(replayState, true, uniformLoc,
2626 uniformCount, false,
2627 uniformBuffer.data()));
2628 }
2629 break;
2630 case GL_FLOAT_MAT3:
2631 for (std::vector<CallCapture> *calls : defaultUniformCalls)
2632 {
2633 Capture(calls, CaptureUniformMatrix3fv(replayState, true, uniformLoc,
2634 uniformCount, false,
2635 uniformBuffer.data()));
2636 }
2637 break;
2638 case GL_FLOAT_MAT2x4:
2639 for (std::vector<CallCapture> *calls : defaultUniformCalls)
2640 {
2641 Capture(calls, CaptureUniformMatrix2x4fv(replayState, true, uniformLoc,
2642 uniformCount, false,
2643 uniformBuffer.data()));
2644 }
2645 break;
2646 case GL_FLOAT_MAT2x3:
2647 for (std::vector<CallCapture> *calls : defaultUniformCalls)
2648 {
2649 Capture(calls, CaptureUniformMatrix2x3fv(replayState, true, uniformLoc,
2650 uniformCount, false,
2651 uniformBuffer.data()));
2652 }
2653 break;
2654 case GL_FLOAT_MAT2:
2655 for (std::vector<CallCapture> *calls : defaultUniformCalls)
2656 {
2657 Capture(calls, CaptureUniformMatrix2fv(replayState, true, uniformLoc,
2658 uniformCount, false,
2659 uniformBuffer.data()));
2660 }
2661 break;
2662 case GL_FLOAT_VEC4:
2663 for (std::vector<CallCapture> *calls : defaultUniformCalls)
2664 {
2665 Capture(calls, CaptureUniform4fv(replayState, true, uniformLoc,
2666 uniformCount, uniformBuffer.data()));
2667 }
2668 break;
2669 case GL_FLOAT_VEC3:
2670 for (std::vector<CallCapture> *calls : defaultUniformCalls)
2671 {
2672 Capture(calls, CaptureUniform3fv(replayState, true, uniformLoc,
2673 uniformCount, uniformBuffer.data()));
2674 }
2675 break;
2676 case GL_FLOAT_VEC2:
2677 for (std::vector<CallCapture> *calls : defaultUniformCalls)
2678 {
2679 Capture(calls, CaptureUniform2fv(replayState, true, uniformLoc,
2680 uniformCount, uniformBuffer.data()));
2681 }
2682 break;
2683 case GL_FLOAT:
2684 for (std::vector<CallCapture> *calls : defaultUniformCalls)
2685 {
2686 Capture(calls, CaptureUniform1fv(replayState, true, uniformLoc,
2687 uniformCount, uniformBuffer.data()));
2688 }
2689 break;
2690 default:
2691 UNIMPLEMENTED();
2692 break;
2693 }
2694 break;
2695 }
2696 case GL_INT:
2697 {
2698 std::vector<GLint> uniformBuffer(uniformSize);
2699 for (int index = 0; index < uniformCount; index++, readLoc.value++)
2700 {
2701 program->getUniformiv(context, readLoc,
2702 uniformBuffer.data() + index * componentCount);
2703 resourceTracker->setDefaultUniformBaseLocation(program->id(), readLoc,
2704 uniformLoc);
2705 }
2706 switch (componentCount)
2707 {
2708 case 4:
2709 for (std::vector<CallCapture> *calls : defaultUniformCalls)
2710 {
2711 Capture(calls, CaptureUniform4iv(replayState, true, uniformLoc,
2712 uniformCount, uniformBuffer.data()));
2713 }
2714 break;
2715 case 3:
2716 for (std::vector<CallCapture> *calls : defaultUniformCalls)
2717 {
2718 Capture(calls, CaptureUniform3iv(replayState, true, uniformLoc,
2719 uniformCount, uniformBuffer.data()));
2720 }
2721 break;
2722 case 2:
2723 for (std::vector<CallCapture> *calls : defaultUniformCalls)
2724 {
2725 Capture(calls, CaptureUniform2iv(replayState, true, uniformLoc,
2726 uniformCount, uniformBuffer.data()));
2727 }
2728 break;
2729 case 1:
2730 for (std::vector<CallCapture> *calls : defaultUniformCalls)
2731 {
2732 Capture(calls, CaptureUniform1iv(replayState, true, uniformLoc,
2733 uniformCount, uniformBuffer.data()));
2734 }
2735 break;
2736 default:
2737 UNIMPLEMENTED();
2738 break;
2739 }
2740 break;
2741 }
2742 case GL_BOOL:
2743 case GL_UNSIGNED_INT:
2744 {
2745 std::vector<GLuint> uniformBuffer(uniformSize);
2746 for (int index = 0; index < uniformCount; index++, readLoc.value++)
2747 {
2748 program->getUniformuiv(context, readLoc,
2749 uniformBuffer.data() + index * componentCount);
2750 resourceTracker->setDefaultUniformBaseLocation(program->id(), readLoc,
2751 uniformLoc);
2752 }
2753 switch (componentCount)
2754 {
2755 case 4:
2756 for (std::vector<CallCapture> *calls : defaultUniformCalls)
2757 {
2758 Capture(calls, CaptureUniform4uiv(replayState, true, uniformLoc,
2759 uniformCount, uniformBuffer.data()));
2760 }
2761 break;
2762 case 3:
2763 for (std::vector<CallCapture> *calls : defaultUniformCalls)
2764 {
2765 Capture(calls, CaptureUniform3uiv(replayState, true, uniformLoc,
2766 uniformCount, uniformBuffer.data()));
2767 }
2768 break;
2769 case 2:
2770 for (std::vector<CallCapture> *calls : defaultUniformCalls)
2771 {
2772 Capture(calls, CaptureUniform2uiv(replayState, true, uniformLoc,
2773 uniformCount, uniformBuffer.data()));
2774 }
2775 break;
2776 case 1:
2777 for (std::vector<CallCapture> *calls : defaultUniformCalls)
2778 {
2779 Capture(calls, CaptureUniform1uiv(replayState, true, uniformLoc,
2780 uniformCount, uniformBuffer.data()));
2781 }
2782 break;
2783 default:
2784 UNIMPLEMENTED();
2785 break;
2786 }
2787 break;
2788 }
2789 default:
2790 UNIMPLEMENTED();
2791 break;
2792 }
2793 }
2794 }
2795
CaptureVertexPointerES1(std::vector<CallCapture> * setupCalls,gl::State * replayState,GLuint attribIndex,const gl::VertexAttribute & attrib,const gl::VertexBinding & binding)2796 void CaptureVertexPointerES1(std::vector<CallCapture> *setupCalls,
2797 gl::State *replayState,
2798 GLuint attribIndex,
2799 const gl::VertexAttribute &attrib,
2800 const gl::VertexBinding &binding)
2801 {
2802 switch (gl::GLES1Renderer::VertexArrayType(attribIndex))
2803 {
2804 case gl::ClientVertexArrayType::Vertex:
2805 Capture(setupCalls,
2806 CaptureVertexPointer(*replayState, true, attrib.format->channelCount,
2807 attrib.format->vertexAttribType, binding.getStride(),
2808 attrib.pointer));
2809 break;
2810 case gl::ClientVertexArrayType::Normal:
2811 Capture(setupCalls,
2812 CaptureNormalPointer(*replayState, true, attrib.format->vertexAttribType,
2813 binding.getStride(), attrib.pointer));
2814 break;
2815 case gl::ClientVertexArrayType::Color:
2816 Capture(setupCalls, CaptureColorPointer(*replayState, true, attrib.format->channelCount,
2817 attrib.format->vertexAttribType,
2818 binding.getStride(), attrib.pointer));
2819 break;
2820 case gl::ClientVertexArrayType::PointSize:
2821 Capture(setupCalls,
2822 CapturePointSizePointerOES(*replayState, true, attrib.format->vertexAttribType,
2823 binding.getStride(), attrib.pointer));
2824 break;
2825 case gl::ClientVertexArrayType::TextureCoord:
2826 Capture(setupCalls,
2827 CaptureTexCoordPointer(*replayState, true, attrib.format->channelCount,
2828 attrib.format->vertexAttribType, binding.getStride(),
2829 attrib.pointer));
2830 break;
2831 default:
2832 UNREACHABLE();
2833 }
2834 }
2835
CaptureTextureEnvironmentState(std::vector<CallCapture> * setupCalls,gl::State * replayState,const gl::State * apiState,unsigned int unit)2836 void CaptureTextureEnvironmentState(std::vector<CallCapture> *setupCalls,
2837 gl::State *replayState,
2838 const gl::State *apiState,
2839 unsigned int unit)
2840 {
2841 const gl::TextureEnvironmentParameters ¤tEnv = apiState->gles1().textureEnvironment(unit);
2842 const gl::TextureEnvironmentParameters &defaultEnv =
2843 replayState->gles1().textureEnvironment(unit);
2844
2845 if (currentEnv == defaultEnv)
2846 {
2847 return;
2848 }
2849
2850 auto capIfNe = [setupCalls](auto currentState, auto defaultState, CallCapture &&call) {
2851 if (currentState != defaultState)
2852 {
2853 setupCalls->emplace_back(std::move(call));
2854 }
2855 };
2856
2857 // When the texture env state differs on a non-default sampler unit, emit an ActiveTexture call.
2858 // The default sampler unit is GL_TEXTURE0.
2859 GLenum currentUnit = GL_TEXTURE0 + static_cast<GLenum>(unit);
2860 GLenum defaultUnit = GL_TEXTURE0 + static_cast<GLenum>(replayState->getActiveSampler());
2861 capIfNe(currentUnit, defaultUnit, CaptureActiveTexture(*replayState, true, currentUnit));
2862
2863 auto capEnum = [capIfNe, replayState](gl::TextureEnvParameter pname, auto currentState,
2864 auto defaultState) {
2865 capIfNe(currentState, defaultState,
2866 CaptureTexEnvi(*replayState, true, gl::TextureEnvTarget::Env, pname,
2867 ToGLenum(currentState)));
2868 };
2869
2870 capEnum(gl::TextureEnvParameter::Mode, currentEnv.mode, defaultEnv.mode);
2871
2872 capEnum(gl::TextureEnvParameter::CombineRgb, currentEnv.combineRgb, defaultEnv.combineRgb);
2873 capEnum(gl::TextureEnvParameter::CombineAlpha, currentEnv.combineAlpha,
2874 defaultEnv.combineAlpha);
2875
2876 capEnum(gl::TextureEnvParameter::Src0Rgb, currentEnv.src0Rgb, defaultEnv.src0Rgb);
2877 capEnum(gl::TextureEnvParameter::Src1Rgb, currentEnv.src1Rgb, defaultEnv.src1Rgb);
2878 capEnum(gl::TextureEnvParameter::Src2Rgb, currentEnv.src2Rgb, defaultEnv.src2Rgb);
2879
2880 capEnum(gl::TextureEnvParameter::Src0Alpha, currentEnv.src0Alpha, defaultEnv.src0Alpha);
2881 capEnum(gl::TextureEnvParameter::Src1Alpha, currentEnv.src1Alpha, defaultEnv.src1Alpha);
2882 capEnum(gl::TextureEnvParameter::Src2Alpha, currentEnv.src2Alpha, defaultEnv.src2Alpha);
2883
2884 capEnum(gl::TextureEnvParameter::Op0Rgb, currentEnv.op0Rgb, defaultEnv.op0Rgb);
2885 capEnum(gl::TextureEnvParameter::Op1Rgb, currentEnv.op1Rgb, defaultEnv.op1Rgb);
2886 capEnum(gl::TextureEnvParameter::Op2Rgb, currentEnv.op2Rgb, defaultEnv.op2Rgb);
2887
2888 capEnum(gl::TextureEnvParameter::Op0Alpha, currentEnv.op0Alpha, defaultEnv.op0Alpha);
2889 capEnum(gl::TextureEnvParameter::Op1Alpha, currentEnv.op1Alpha, defaultEnv.op1Alpha);
2890 capEnum(gl::TextureEnvParameter::Op2Alpha, currentEnv.op2Alpha, defaultEnv.op2Alpha);
2891
2892 auto capFloat = [capIfNe, replayState](gl::TextureEnvParameter pname, auto currentState,
2893 auto defaultState) {
2894 capIfNe(currentState, defaultState,
2895 CaptureTexEnvf(*replayState, true, gl::TextureEnvTarget::Env, pname, currentState));
2896 };
2897
2898 capFloat(gl::TextureEnvParameter::RgbScale, currentEnv.rgbScale, defaultEnv.rgbScale);
2899 capFloat(gl::TextureEnvParameter::AlphaScale, currentEnv.alphaScale, defaultEnv.alphaScale);
2900
2901 capIfNe(currentEnv.color, defaultEnv.color,
2902 CaptureTexEnvfv(*replayState, true, gl::TextureEnvTarget::Env,
2903 gl::TextureEnvParameter::Color, currentEnv.color.data()));
2904
2905 // PointCoordReplace is the only parameter that uses the PointSprite TextureEnvTarget.
2906 capIfNe(currentEnv.pointSpriteCoordReplace, defaultEnv.pointSpriteCoordReplace,
2907 CaptureTexEnvi(*replayState, true, gl::TextureEnvTarget::PointSprite,
2908 gl::TextureEnvParameter::PointCoordReplace,
2909 currentEnv.pointSpriteCoordReplace));
2910
2911 // In case of non-default sampler units, the default unit must be set back here.
2912 capIfNe(currentUnit, defaultUnit, CaptureActiveTexture(*replayState, true, defaultUnit));
2913 }
2914
VertexBindingMatchesAttribStride(const gl::VertexAttribute & attrib,const gl::VertexBinding & binding)2915 bool VertexBindingMatchesAttribStride(const gl::VertexAttribute &attrib,
2916 const gl::VertexBinding &binding)
2917 {
2918 if (attrib.vertexAttribArrayStride == 0 &&
2919 binding.getStride() == ComputeVertexAttributeTypeSize(attrib))
2920 {
2921 return true;
2922 }
2923
2924 return attrib.vertexAttribArrayStride == binding.getStride();
2925 }
2926
CaptureVertexArrayState(std::vector<CallCapture> * setupCalls,const gl::Context * context,const gl::VertexArray * vertexArray,gl::State * replayState)2927 void CaptureVertexArrayState(std::vector<CallCapture> *setupCalls,
2928 const gl::Context *context,
2929 const gl::VertexArray *vertexArray,
2930 gl::State *replayState)
2931 {
2932 const std::vector<gl::VertexAttribute> &vertexAttribs = vertexArray->getVertexAttributes();
2933 const std::vector<gl::VertexBinding> &vertexBindings = vertexArray->getVertexBindings();
2934
2935 gl::AttributesMask vertexPointerBindings;
2936
2937 ASSERT(vertexAttribs.size() <= vertexBindings.size());
2938 for (GLuint attribIndex = 0; attribIndex < vertexAttribs.size(); ++attribIndex)
2939 {
2940 const gl::VertexAttribute defaultAttrib(attribIndex);
2941 const gl::VertexBinding defaultBinding;
2942
2943 const gl::VertexAttribute &attrib = vertexAttribs[attribIndex];
2944 const gl::VertexBinding &binding = vertexBindings[attrib.bindingIndex];
2945
2946 if (attrib.enabled != defaultAttrib.enabled)
2947 {
2948 if (context->isGLES1())
2949 {
2950 Capture(setupCalls,
2951 CaptureEnableClientState(*replayState, false,
2952 gl::GLES1Renderer::VertexArrayType(attribIndex)));
2953 }
2954 else
2955 {
2956 Capture(setupCalls,
2957 CaptureEnableVertexAttribArray(*replayState, false, attribIndex));
2958 }
2959 }
2960
2961 // Don't capture CaptureVertexAttribPointer calls when a non-default VAO is bound, the array
2962 // buffer is null and a non-null attrib pointer is used.
2963 bool skipInvalidAttrib = vertexArray->id().value != 0 &&
2964 binding.getBuffer().get() == nullptr && attrib.pointer != nullptr;
2965
2966 if (!skipInvalidAttrib &&
2967 (attrib.format != defaultAttrib.format || attrib.pointer != defaultAttrib.pointer ||
2968 binding.getStride() != defaultBinding.getStride() ||
2969 attrib.bindingIndex != defaultAttrib.bindingIndex ||
2970 binding.getBuffer().get() != nullptr))
2971 {
2972 // Each attribute can pull from a separate buffer, so check the binding
2973 gl::Buffer *buffer = binding.getBuffer().get();
2974 if (buffer != replayState->getArrayBuffer())
2975 {
2976 replayState->setBufferBinding(context, gl::BufferBinding::Array, buffer);
2977
2978 gl::BufferID bufferID = {0};
2979 if (buffer)
2980 {
2981 bufferID = buffer->id();
2982 }
2983 Capture(setupCalls,
2984 CaptureBindBuffer(*replayState, true, gl::BufferBinding::Array, bufferID));
2985 }
2986
2987 // Establish the relationship between currently bound buffer and the VAO
2988 if (context->isGLES1())
2989 {
2990 // Track indexes that used ES1 calls
2991 vertexPointerBindings.set(attribIndex);
2992
2993 CaptureVertexPointerES1(setupCalls, replayState, attribIndex, attrib, binding);
2994 }
2995 else if (attrib.bindingIndex == attribIndex &&
2996 VertexBindingMatchesAttribStride(attrib, binding) &&
2997 (!buffer || binding.getOffset() == reinterpret_cast<GLintptr>(attrib.pointer)))
2998 {
2999 // Check if we can use strictly ES2 semantics, and track indexes that do.
3000 vertexPointerBindings.set(attribIndex);
3001
3002 if (attrib.format->isPureInt())
3003 {
3004 Capture(setupCalls, CaptureVertexAttribIPointer(*replayState, true, attribIndex,
3005 attrib.format->channelCount,
3006 attrib.format->vertexAttribType,
3007 attrib.vertexAttribArrayStride,
3008 attrib.pointer));
3009 }
3010 else
3011 {
3012 Capture(setupCalls,
3013 CaptureVertexAttribPointer(
3014 *replayState, true, attribIndex, attrib.format->channelCount,
3015 attrib.format->vertexAttribType, attrib.format->isNorm(),
3016 attrib.vertexAttribArrayStride, attrib.pointer));
3017 }
3018
3019 if (binding.getDivisor() != 0)
3020 {
3021 Capture(setupCalls, CaptureVertexAttribDivisor(*replayState, true, attribIndex,
3022 binding.getDivisor()));
3023 }
3024 }
3025 else
3026 {
3027 ASSERT(context->getClientVersion() >= gl::ES_3_1);
3028
3029 if (attrib.format->isPureInt())
3030 {
3031 Capture(setupCalls, CaptureVertexAttribIFormat(*replayState, true, attribIndex,
3032 attrib.format->channelCount,
3033 attrib.format->vertexAttribType,
3034 attrib.relativeOffset));
3035 }
3036 else
3037 {
3038 Capture(setupCalls, CaptureVertexAttribFormat(*replayState, true, attribIndex,
3039 attrib.format->channelCount,
3040 attrib.format->vertexAttribType,
3041 attrib.format->isNorm(),
3042 attrib.relativeOffset));
3043 }
3044
3045 Capture(setupCalls, CaptureVertexAttribBinding(*replayState, true, attribIndex,
3046 attrib.bindingIndex));
3047 }
3048 }
3049 }
3050
3051 // The loop below expects attribs and bindings to have equal counts
3052 static_assert(gl::MAX_VERTEX_ATTRIBS == gl::MAX_VERTEX_ATTRIB_BINDINGS,
3053 "Max vertex attribs and bindings count mismatch");
3054
3055 // Loop through binding indices that weren't used by VertexAttribPointer
3056 for (size_t bindingIndex : vertexPointerBindings.flip())
3057 {
3058 const gl::VertexBinding &binding = vertexBindings[bindingIndex];
3059
3060 if (binding.getBuffer().id().value != 0)
3061 {
3062 Capture(setupCalls,
3063 CaptureBindVertexBuffer(*replayState, true, static_cast<GLuint>(bindingIndex),
3064 binding.getBuffer().id(), binding.getOffset(),
3065 binding.getStride()));
3066 }
3067
3068 if (binding.getDivisor() != 0)
3069 {
3070 Capture(setupCalls, CaptureVertexBindingDivisor(*replayState, true,
3071 static_cast<GLuint>(bindingIndex),
3072 binding.getDivisor()));
3073 }
3074 }
3075
3076 // The element array buffer is not per attribute, but per VAO
3077 gl::Buffer *elementArrayBuffer = vertexArray->getElementArrayBuffer();
3078 if (elementArrayBuffer)
3079 {
3080 Capture(setupCalls, CaptureBindBuffer(*replayState, true, gl::BufferBinding::ElementArray,
3081 elementArrayBuffer->id()));
3082 }
3083 }
3084
CaptureTextureStorage(std::vector<CallCapture> * setupCalls,gl::State * replayState,const gl::Texture * texture)3085 void CaptureTextureStorage(std::vector<CallCapture> *setupCalls,
3086 gl::State *replayState,
3087 const gl::Texture *texture)
3088 {
3089 // Use mip-level 0 for the base dimensions
3090 gl::ImageIndex imageIndex = gl::ImageIndex::MakeFromType(texture->getType(), 0);
3091 const gl::ImageDesc &desc = texture->getTextureState().getImageDesc(imageIndex);
3092
3093 switch (texture->getType())
3094 {
3095 case gl::TextureType::_2D:
3096 case gl::TextureType::CubeMap:
3097 {
3098 Capture(setupCalls, CaptureTexStorage2D(*replayState, true, texture->getType(),
3099 texture->getImmutableLevels(),
3100 desc.format.info->internalFormat,
3101 desc.size.width, desc.size.height));
3102 break;
3103 }
3104 case gl::TextureType::_3D:
3105 case gl::TextureType::_2DArray:
3106 case gl::TextureType::CubeMapArray:
3107 {
3108 Capture(setupCalls, CaptureTexStorage3D(
3109 *replayState, true, texture->getType(),
3110 texture->getImmutableLevels(), desc.format.info->internalFormat,
3111 desc.size.width, desc.size.height, desc.size.depth));
3112 break;
3113 }
3114 case gl::TextureType::Buffer:
3115 {
3116 // Do nothing. This will already be captured as a buffer.
3117 break;
3118 }
3119 default:
3120 UNIMPLEMENTED();
3121 break;
3122 }
3123 }
3124
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)3125 void CaptureTextureContents(std::vector<CallCapture> *setupCalls,
3126 gl::State *replayState,
3127 const gl::Texture *texture,
3128 const gl::ImageIndex &index,
3129 const gl::ImageDesc &desc,
3130 GLuint size,
3131 const void *data)
3132 {
3133 const gl::InternalFormat &format = *desc.format.info;
3134
3135 if (index.getType() == gl::TextureType::Buffer)
3136 {
3137 // Zero binding size indicates full buffer bound
3138 if (texture->getBuffer().getSize() == 0)
3139 {
3140 Capture(setupCalls,
3141 CaptureTexBufferEXT(*replayState, true, index.getType(), format.internalFormat,
3142 texture->getBuffer().get()->id()));
3143 }
3144 else
3145 {
3146 Capture(setupCalls, CaptureTexBufferRangeEXT(*replayState, true, index.getType(),
3147 format.internalFormat,
3148 texture->getBuffer().get()->id(),
3149 texture->getBuffer().getOffset(),
3150 texture->getBuffer().getSize()));
3151 }
3152
3153 // For buffers, we're done
3154 return;
3155 }
3156
3157 if (index.getType() == gl::TextureType::External)
3158 {
3159 // The generated glTexImage2D call is for creating the staging texture
3160 Capture(setupCalls,
3161 CaptureTexImage2D(*replayState, true, gl::TextureTarget::_2D, index.getLevelIndex(),
3162 format.internalFormat, desc.size.width, desc.size.height, 0,
3163 format.format, format.type, data));
3164
3165 // For external textures, we're done
3166 return;
3167 }
3168
3169 bool is3D =
3170 (index.getType() == gl::TextureType::_3D || index.getType() == gl::TextureType::_2DArray ||
3171 index.getType() == gl::TextureType::CubeMapArray);
3172
3173 if (format.compressed || format.paletted)
3174 {
3175 if (is3D)
3176 {
3177 if (texture->getImmutableFormat())
3178 {
3179 Capture(setupCalls,
3180 CaptureCompressedTexSubImage3D(
3181 *replayState, true, index.getTarget(), index.getLevelIndex(), 0, 0, 0,
3182 desc.size.width, desc.size.height, desc.size.depth,
3183 format.internalFormat, size, data));
3184 }
3185 else
3186 {
3187 Capture(setupCalls,
3188 CaptureCompressedTexImage3D(*replayState, true, index.getTarget(),
3189 index.getLevelIndex(), format.internalFormat,
3190 desc.size.width, desc.size.height,
3191 desc.size.depth, 0, size, data));
3192 }
3193 }
3194 else
3195 {
3196 if (texture->getImmutableFormat())
3197 {
3198 Capture(setupCalls,
3199 CaptureCompressedTexSubImage2D(
3200 *replayState, true, index.getTarget(), index.getLevelIndex(), 0, 0,
3201 desc.size.width, desc.size.height, format.internalFormat, size, data));
3202 }
3203 else
3204 {
3205 Capture(setupCalls, CaptureCompressedTexImage2D(
3206 *replayState, true, index.getTarget(),
3207 index.getLevelIndex(), format.internalFormat,
3208 desc.size.width, desc.size.height, 0, size, data));
3209 }
3210 }
3211 }
3212 else
3213 {
3214 if (is3D)
3215 {
3216 if (texture->getImmutableFormat())
3217 {
3218 Capture(setupCalls,
3219 CaptureTexSubImage3D(*replayState, true, index.getTarget(),
3220 index.getLevelIndex(), 0, 0, 0, desc.size.width,
3221 desc.size.height, desc.size.depth, format.format,
3222 format.type, data));
3223 }
3224 else
3225 {
3226 Capture(
3227 setupCalls,
3228 CaptureTexImage3D(*replayState, true, index.getTarget(), index.getLevelIndex(),
3229 format.internalFormat, desc.size.width, desc.size.height,
3230 desc.size.depth, 0, format.format, format.type, data));
3231 }
3232 }
3233 else
3234 {
3235 if (texture->getImmutableFormat())
3236 {
3237 Capture(setupCalls,
3238 CaptureTexSubImage2D(*replayState, true, index.getTarget(),
3239 index.getLevelIndex(), 0, 0, desc.size.width,
3240 desc.size.height, format.format, format.type, data));
3241 }
3242 else
3243 {
3244 Capture(setupCalls, CaptureTexImage2D(*replayState, true, index.getTarget(),
3245 index.getLevelIndex(), format.internalFormat,
3246 desc.size.width, desc.size.height, 0,
3247 format.format, format.type, data));
3248 }
3249 }
3250 }
3251 }
3252
CaptureCustomUniformBlockBinding(const CallCapture & callIn,std::vector<CallCapture> & callsOut)3253 void CaptureCustomUniformBlockBinding(const CallCapture &callIn, std::vector<CallCapture> &callsOut)
3254 {
3255 const ParamBuffer ¶msIn = callIn.params;
3256
3257 const ParamCapture &programID =
3258 paramsIn.getParam("programPacked", ParamType::TShaderProgramID, 0);
3259 const ParamCapture &blockIndex =
3260 paramsIn.getParam("uniformBlockIndexPacked", ParamType::TUniformBlockIndex, 1);
3261 const ParamCapture &blockBinding =
3262 paramsIn.getParam("uniformBlockBinding", ParamType::TGLuint, 2);
3263
3264 ParamBuffer params;
3265 params.addValueParam("program", ParamType::TGLuint, programID.value.ShaderProgramIDVal.value);
3266 params.addValueParam("uniformBlockIndex", ParamType::TGLuint,
3267 blockIndex.value.UniformBlockIndexVal.value);
3268 params.addValueParam("uniformBlockBinding", ParamType::TGLuint, blockBinding.value.GLuintVal);
3269
3270 callsOut.emplace_back("UniformBlockBinding", std::move(params));
3271 }
3272
CaptureCustomMapBuffer(const char * entryPointName,CallCapture & call,std::vector<CallCapture> & callsOut,gl::BufferID mappedBufferID)3273 void CaptureCustomMapBuffer(const char *entryPointName,
3274 CallCapture &call,
3275 std::vector<CallCapture> &callsOut,
3276 gl::BufferID mappedBufferID)
3277 {
3278 call.params.addValueParam("buffer", ParamType::TGLuint, mappedBufferID.value);
3279 callsOut.emplace_back(entryPointName, std::move(call.params));
3280 }
3281
CaptureCustomShaderProgram(const char * name,CallCapture & call,std::vector<CallCapture> & callsOut)3282 void CaptureCustomShaderProgram(const char *name,
3283 CallCapture &call,
3284 std::vector<CallCapture> &callsOut)
3285 {
3286 call.params.addValueParam("shaderProgram", ParamType::TGLuint,
3287 call.params.getReturnValue().value.GLuintVal);
3288 call.customFunctionName = name;
3289 callsOut.emplace_back(std::move(call));
3290 }
3291
CaptureCustomFenceSync(CallCapture & call,std::vector<CallCapture> & callsOut)3292 void CaptureCustomFenceSync(CallCapture &call, std::vector<CallCapture> &callsOut)
3293 {
3294 ParamBuffer &¶ms = std::move(call.params);
3295 params.addValueParam("fenceSync", ParamType::TGLuint64,
3296 params.getReturnValue().value.GLuint64Val);
3297 call.customFunctionName = "FenceSync2";
3298 callsOut.emplace_back(std::move(call));
3299 }
3300
CaptureCustomCreateEGLImage(const char * name,CallCapture & call,std::vector<CallCapture> & callsOut)3301 void CaptureCustomCreateEGLImage(const char *name,
3302 CallCapture &call,
3303 std::vector<CallCapture> &callsOut)
3304 {
3305 ParamBuffer &¶ms = std::move(call.params);
3306 EGLImage returnVal = params.getReturnValue().value.EGLImageVal;
3307 egl::ImageID imageID = egl::PackParam<egl::ImageID>(returnVal);
3308 params.addValueParam("image", ParamType::TGLuint, imageID.value);
3309 call.customFunctionName = name;
3310 callsOut.emplace_back(std::move(call));
3311 }
3312
CaptureCustomCreateEGLSync(const char * name,CallCapture & call,std::vector<CallCapture> & callsOut)3313 void CaptureCustomCreateEGLSync(const char *name,
3314 CallCapture &call,
3315 std::vector<CallCapture> &callsOut)
3316 {
3317 ParamBuffer &¶ms = std::move(call.params);
3318 EGLSync returnVal = params.getReturnValue().value.EGLSyncVal;
3319 egl::SyncID syncID = egl::PackParam<egl::SyncID>(returnVal);
3320 params.addValueParam("sync", ParamType::TGLuint, syncID.value);
3321 call.customFunctionName = name;
3322 callsOut.emplace_back(std::move(call));
3323 }
3324
CaptureCustomCreatePbufferSurface(CallCapture & call,std::vector<CallCapture> & callsOut)3325 void CaptureCustomCreatePbufferSurface(CallCapture &call, std::vector<CallCapture> &callsOut)
3326 {
3327 ParamBuffer &¶ms = std::move(call.params);
3328 EGLSurface returnVal = params.getReturnValue().value.EGLSurfaceVal;
3329 egl::SurfaceID surfaceID = egl::PackParam<egl::SurfaceID>(returnVal);
3330
3331 params.addValueParam("surface", ParamType::TGLuint, surfaceID.value);
3332 call.customFunctionName = "CreatePbufferSurface";
3333 callsOut.emplace_back(std::move(call));
3334 }
3335
CaptureCustomCreateNativeClientbuffer(CallCapture & call,std::vector<CallCapture> & callsOut)3336 void CaptureCustomCreateNativeClientbuffer(CallCapture &call, std::vector<CallCapture> &callsOut)
3337 {
3338 ParamBuffer &¶ms = std::move(call.params);
3339 params.addValueParam("clientBuffer", ParamType::TEGLClientBuffer,
3340 params.getReturnValue().value.EGLClientBufferVal);
3341 call.customFunctionName = "CreateNativeClientBufferANDROID";
3342 callsOut.emplace_back(std::move(call));
3343 }
3344
GenerateLinkedProgram(const gl::Context * context,const gl::State & replayState,ResourceTracker * resourceTracker,std::vector<CallCapture> * setupCalls,gl::Program * program,gl::ShaderProgramID id,gl::ShaderProgramID tempIDStart,const ProgramSources & linkedSources)3345 void GenerateLinkedProgram(const gl::Context *context,
3346 const gl::State &replayState,
3347 ResourceTracker *resourceTracker,
3348 std::vector<CallCapture> *setupCalls,
3349 gl::Program *program,
3350 gl::ShaderProgramID id,
3351 gl::ShaderProgramID tempIDStart,
3352 const ProgramSources &linkedSources)
3353 {
3354 // A map to store the gShaderProgram map lookup index of the temp shaders we attached below. We
3355 // need this map to retrieve the lookup index to pass to CaptureDetachShader calls at the end of
3356 // GenerateLinkedProgram.
3357 PackedEnumMap<gl::ShaderType, gl::ShaderProgramID> tempShaderIDTracker;
3358
3359 // Compile with last linked sources.
3360 for (gl::ShaderType shaderType : program->getExecutable().getLinkedShaderStages())
3361 {
3362 // Bump the max shader program id for each new tempIDStart we use to create, compile, and
3363 // attach the temp shader object.
3364 resourceTracker->onShaderProgramAccess(tempIDStart);
3365 // Store the tempIDStart in the tempShaderIDTracker to retrieve for CaptureDetachShader
3366 // calls later.
3367 tempShaderIDTracker[shaderType] = tempIDStart;
3368 const std::string &sourceString = linkedSources[shaderType];
3369 const char *sourcePointer = sourceString.c_str();
3370
3371 if (sourceString.empty())
3372 {
3373 // If we don't have source for this shader, that means it was populated by the app
3374 // using glProgramBinary. We need to look it up from our cached copy.
3375 const ProgramSources &cachedLinkedSources =
3376 context->getShareGroup()->getFrameCaptureShared()->getProgramSources(id);
3377
3378 const std::string &cachedSourceString = cachedLinkedSources[shaderType];
3379 sourcePointer = cachedSourceString.c_str();
3380 ASSERT(!cachedSourceString.empty());
3381 }
3382
3383 // Compile and attach the temporary shader. Then free it immediately.
3384 CallCapture createShader =
3385 CaptureCreateShader(replayState, true, shaderType, tempIDStart.value);
3386 CaptureCustomShaderProgram("CreateShader", createShader, *setupCalls);
3387 Capture(setupCalls,
3388 CaptureShaderSource(replayState, true, tempIDStart, 1, &sourcePointer, nullptr));
3389 Capture(setupCalls, CaptureCompileShader(replayState, true, tempIDStart));
3390 Capture(setupCalls, CaptureAttachShader(replayState, true, id, tempIDStart));
3391 // Increment tempIDStart to get a new gShaderProgram map index for the next linked stage
3392 // shader object. We can't reuse the same tempIDStart as we need to retrieve the index of
3393 // each attached shader object later to pass to CaptureDetachShader calls.
3394 tempIDStart.value += 1;
3395 }
3396
3397 // Gather XFB varyings
3398 std::vector<std::string> xfbVaryings;
3399 for (const gl::TransformFeedbackVarying &xfbVarying :
3400 program->getState().getLinkedTransformFeedbackVaryings())
3401 {
3402 xfbVaryings.push_back(xfbVarying.nameWithArrayIndex());
3403 }
3404
3405 if (!xfbVaryings.empty())
3406 {
3407 std::vector<const char *> varyingsStrings;
3408 for (const std::string &varyingString : xfbVaryings)
3409 {
3410 varyingsStrings.push_back(varyingString.data());
3411 }
3412
3413 GLenum xfbMode = program->getState().getTransformFeedbackBufferMode();
3414 Capture(setupCalls, CaptureTransformFeedbackVaryings(replayState, true, id,
3415 static_cast<GLint>(xfbVaryings.size()),
3416 varyingsStrings.data(), xfbMode));
3417 }
3418
3419 // Force the attributes to be bound the same way as in the existing program.
3420 // This can affect attributes that are optimized out in some implementations.
3421 for (const sh::ShaderVariable &attrib : program->getState().getProgramInputs())
3422 {
3423 if (gl::IsBuiltInName(attrib.name))
3424 {
3425 // Don't try to bind built-in attributes
3426 continue;
3427 }
3428
3429 // Separable programs may not have a VS, meaning it may not have attributes.
3430 if (program->getExecutable().hasLinkedShaderStage(gl::ShaderType::Vertex))
3431 {
3432 ASSERT(attrib.location != -1);
3433 Capture(setupCalls, CaptureBindAttribLocation(replayState, true, id,
3434 static_cast<GLuint>(attrib.location),
3435 attrib.name.c_str()));
3436 }
3437 }
3438
3439 if (program->isSeparable())
3440 {
3441 // MEC manually recreates separable programs, rather than attempting to recreate a call
3442 // to glCreateShaderProgramv(), so insert a call to mark it separable.
3443 Capture(setupCalls,
3444 CaptureProgramParameteri(replayState, true, id, GL_PROGRAM_SEPARABLE, GL_TRUE));
3445 }
3446
3447 Capture(setupCalls, CaptureLinkProgram(replayState, true, id));
3448 CaptureUpdateUniformLocations(program, setupCalls);
3449 CaptureUpdateUniformValues(replayState, context, program, resourceTracker, setupCalls);
3450 CaptureUpdateUniformBlockIndexes(program, setupCalls);
3451
3452 // Capture uniform block bindings for each program
3453 for (unsigned int uniformBlockIndex = 0;
3454 uniformBlockIndex < program->getActiveUniformBlockCount(); uniformBlockIndex++)
3455 {
3456 GLuint blockBinding = program->getUniformBlockBinding(uniformBlockIndex);
3457 CallCapture updateCallCapture =
3458 CaptureUniformBlockBinding(replayState, true, id, {uniformBlockIndex}, blockBinding);
3459 CaptureCustomUniformBlockBinding(updateCallCapture, *setupCalls);
3460 }
3461
3462 // Add DetachShader call if that's what the app does, so that the
3463 // ResourceManagerBase::mHandleAllocator can release the ShaderProgramID handle assigned to the
3464 // shader object when glDeleteShader is called. This ensures the ShaderProgramID handles used in
3465 // SetupReplayContextShared() are consistent with the ShaderProgramID handles used by the app.
3466 for (gl::ShaderType shaderType : program->getExecutable().getLinkedShaderStages())
3467 {
3468 gl::Shader *attachedShader = program->getAttachedShader(shaderType);
3469 if (attachedShader == nullptr)
3470 {
3471 Capture(setupCalls,
3472 CaptureDetachShader(replayState, true, id, tempShaderIDTracker[shaderType]));
3473 }
3474 Capture(setupCalls,
3475 CaptureDeleteShader(replayState, true, tempShaderIDTracker[shaderType]));
3476 }
3477 }
3478
3479 // TODO(http://anglebug.com/4599): Improve reset/restore call generation
3480 // There are multiple ways to track reset calls for individual resources. For now, we are tracking
3481 // separate lists of instructions that mirror the calls created during mid-execution setup. Other
3482 // methods could involve passing the original CallCaptures to this function, or tracking the
3483 // indices of original setup calls.
CaptureBufferResetCalls(const gl::Context * context,const gl::State & replayState,ResourceTracker * resourceTracker,gl::BufferID * id,const gl::Buffer * buffer)3484 void CaptureBufferResetCalls(const gl::Context *context,
3485 const gl::State &replayState,
3486 ResourceTracker *resourceTracker,
3487 gl::BufferID *id,
3488 const gl::Buffer *buffer)
3489 {
3490 GLuint bufferID = (*id).value;
3491
3492 // Track this as a starting resource that may need to be restored.
3493 TrackedResource &trackedBuffers =
3494 resourceTracker->getTrackedResource(context->id(), ResourceIDType::Buffer);
3495
3496 // Track calls to regenerate a given buffer
3497 ResourceCalls &bufferRegenCalls = trackedBuffers.getResourceRegenCalls();
3498 Capture(&bufferRegenCalls[bufferID], CaptureGenBuffers(replayState, true, 1, id));
3499 MaybeCaptureUpdateResourceIDs(context, resourceTracker, &bufferRegenCalls[bufferID]);
3500
3501 // Call glBufferStorageEXT when regenerating immutable buffers,
3502 // as we can't call glBufferData on restore.
3503 if (buffer->isImmutable())
3504 {
3505 Capture(&bufferRegenCalls[bufferID],
3506 CaptureBindBuffer(replayState, true, gl::BufferBinding::Array, *id));
3507 Capture(
3508 &bufferRegenCalls[bufferID],
3509 CaptureBufferStorageEXT(replayState, true, gl::BufferBinding::Array,
3510 static_cast<GLsizeiptr>(buffer->getSize()),
3511 buffer->getMapPointer(), buffer->getStorageExtUsageFlags()));
3512 }
3513
3514 // Track calls to restore a given buffer's contents
3515 ResourceCalls &bufferRestoreCalls = trackedBuffers.getResourceRestoreCalls();
3516 Capture(&bufferRestoreCalls[bufferID],
3517 CaptureBindBuffer(replayState, true, gl::BufferBinding::Array, *id));
3518
3519 // Mutable buffers will be restored here using glBufferData.
3520 // Immutable buffers need to be restored below, after maping.
3521 if (!buffer->isImmutable())
3522 {
3523 Capture(&bufferRestoreCalls[bufferID],
3524 CaptureBufferData(replayState, true, gl::BufferBinding::Array,
3525 static_cast<GLsizeiptr>(buffer->getSize()),
3526 buffer->getMapPointer(), buffer->getUsage()));
3527 }
3528
3529 if (buffer->isMapped())
3530 {
3531 // Track calls to remap a buffer that started as mapped
3532 BufferCalls &bufferMapCalls = resourceTracker->getBufferMapCalls();
3533
3534 Capture(&bufferMapCalls[bufferID],
3535 CaptureBindBuffer(replayState, true, gl::BufferBinding::Array, *id));
3536
3537 void *dontCare = nullptr;
3538 CallCapture mapBufferRange = CaptureMapBufferRange(
3539 replayState, true, gl::BufferBinding::Array,
3540 static_cast<GLsizeiptr>(buffer->getMapOffset()),
3541 static_cast<GLsizeiptr>(buffer->getMapLength()), buffer->getAccessFlags(), dontCare);
3542 CaptureCustomMapBuffer("MapBufferRange", mapBufferRange, bufferMapCalls[bufferID],
3543 buffer->id());
3544
3545 // Restore immutable mapped buffers. Needs to happen after mapping.
3546 if (buffer->isImmutable())
3547 {
3548 ParamBuffer dataParamBuffer;
3549 dataParamBuffer.addValueParam("dest", ParamType::TGLuint, buffer->id().value);
3550 ParamCapture captureData("source", ParamType::TvoidConstPointer);
3551 CaptureMemory(buffer->getMapPointer(), static_cast<GLsizeiptr>(buffer->getSize()),
3552 &captureData);
3553 dataParamBuffer.addParam(std::move(captureData));
3554 dataParamBuffer.addValueParam<GLsizeiptr>("size", ParamType::TGLsizeiptr,
3555 static_cast<GLsizeiptr>(buffer->getSize()));
3556 bufferMapCalls[bufferID].emplace_back("UpdateClientBufferData",
3557 std::move(dataParamBuffer));
3558 }
3559 }
3560
3561 // Track calls unmap a buffer that started as unmapped
3562 BufferCalls &bufferUnmapCalls = resourceTracker->getBufferUnmapCalls();
3563 Capture(&bufferUnmapCalls[bufferID],
3564 CaptureBindBuffer(replayState, true, gl::BufferBinding::Array, *id));
3565 Capture(&bufferUnmapCalls[bufferID],
3566 CaptureUnmapBuffer(replayState, true, gl::BufferBinding::Array, GL_TRUE));
3567 }
3568
CaptureFenceSyncResetCalls(const gl::Context * context,const gl::State & replayState,ResourceTracker * resourceTracker,gl::SyncID syncID,GLsync syncObject,const gl::Sync * sync)3569 void CaptureFenceSyncResetCalls(const gl::Context *context,
3570 const gl::State &replayState,
3571 ResourceTracker *resourceTracker,
3572 gl::SyncID syncID,
3573 GLsync syncObject,
3574 const gl::Sync *sync)
3575 {
3576 // Track calls to regenerate a given fence sync
3577 FenceSyncCalls &fenceSyncRegenCalls = resourceTracker->getFenceSyncRegenCalls();
3578 CallCapture fenceSync =
3579 CaptureFenceSync(replayState, true, sync->getCondition(), sync->getFlags(), syncObject);
3580 CaptureCustomFenceSync(fenceSync, fenceSyncRegenCalls[syncID]);
3581 MaybeCaptureUpdateResourceIDs(context, resourceTracker, &fenceSyncRegenCalls[syncID]);
3582 }
3583
CaptureEGLSyncResetCalls(const gl::Context * context,const gl::State & replayState,ResourceTracker * resourceTracker,egl::SyncID eglSyncID,EGLSync eglSyncObject,const egl::Sync * eglSync)3584 void CaptureEGLSyncResetCalls(const gl::Context *context,
3585 const gl::State &replayState,
3586 ResourceTracker *resourceTracker,
3587 egl::SyncID eglSyncID,
3588 EGLSync eglSyncObject,
3589 const egl::Sync *eglSync)
3590 {
3591 // Track this as a starting resource that may need to be restored.
3592 TrackedResource &trackedEGLSyncs =
3593 resourceTracker->getTrackedResource(context->id(), ResourceIDType::egl_Sync);
3594
3595 // Track calls to regenerate a given buffer
3596 ResourceCalls &eglSyncRegenCalls = trackedEGLSyncs.getResourceRegenCalls();
3597
3598 CallCapture createEGLSync =
3599 CaptureCreateSyncKHR(nullptr, true, context->getDisplay(), eglSync->getType(),
3600 eglSync->getAttributeMap(), eglSyncObject);
3601 CaptureCustomCreateEGLSync("CreateEGLSyncKHR", createEGLSync,
3602 eglSyncRegenCalls[eglSyncID.value]);
3603 MaybeCaptureUpdateResourceIDs(context, resourceTracker, &eglSyncRegenCalls[eglSyncID.value]);
3604 }
3605
CaptureBufferBindingResetCalls(const gl::State & replayState,ResourceTracker * resourceTracker,gl::BufferBinding binding,gl::BufferID id)3606 void CaptureBufferBindingResetCalls(const gl::State &replayState,
3607 ResourceTracker *resourceTracker,
3608 gl::BufferBinding binding,
3609 gl::BufferID id)
3610 {
3611 std::vector<CallCapture> &bufferBindingCalls = resourceTracker->getBufferBindingCalls();
3612 Capture(&bufferBindingCalls, CaptureBindBuffer(replayState, true, binding, id));
3613 }
3614
CaptureIndexedBuffers(const gl::State & glState,const gl::BufferVector & indexedBuffers,gl::BufferBinding binding,std::vector<CallCapture> * setupCalls)3615 void CaptureIndexedBuffers(const gl::State &glState,
3616 const gl::BufferVector &indexedBuffers,
3617 gl::BufferBinding binding,
3618 std::vector<CallCapture> *setupCalls)
3619 {
3620 for (unsigned int index = 0; index < indexedBuffers.size(); ++index)
3621 {
3622 const gl::OffsetBindingPointer<gl::Buffer> &buffer = indexedBuffers[index];
3623
3624 if (buffer.get() == nullptr)
3625 {
3626 continue;
3627 }
3628
3629 GLintptr offset = buffer.getOffset();
3630 GLsizeiptr size = buffer.getSize();
3631 gl::BufferID bufferID = buffer.get()->id();
3632
3633 // Context::bindBufferBase() calls Context::bindBufferRange() with size and offset = 0.
3634 if ((offset == 0) && (size == 0))
3635 {
3636 Capture(setupCalls, CaptureBindBufferBase(glState, true, binding, index, bufferID));
3637 }
3638 else
3639 {
3640 Capture(setupCalls,
3641 CaptureBindBufferRange(glState, true, binding, index, bufferID, offset, size));
3642 }
3643 }
3644 }
3645
CaptureDefaultVertexAttribs(const gl::State & replayState,const gl::State & apiState,std::vector<CallCapture> * setupCalls)3646 void CaptureDefaultVertexAttribs(const gl::State &replayState,
3647 const gl::State &apiState,
3648 std::vector<CallCapture> *setupCalls)
3649 {
3650 const std::vector<gl::VertexAttribCurrentValueData> ¤tValues =
3651 apiState.getVertexAttribCurrentValues();
3652
3653 for (GLuint attribIndex = 0; attribIndex < currentValues.size(); ++attribIndex)
3654 {
3655 const gl::VertexAttribCurrentValueData &defaultValue = currentValues[attribIndex];
3656 if (!IsDefaultCurrentValue(defaultValue))
3657 {
3658 Capture(setupCalls, CaptureVertexAttrib4fv(replayState, true, attribIndex,
3659 defaultValue.Values.FloatValues));
3660 }
3661 }
3662 }
3663
CompressPalettedTexture(angle::MemoryBuffer & data,angle::MemoryBuffer & tmp,const gl::InternalFormat & compressedFormat,const gl::Extents & extents)3664 void CompressPalettedTexture(angle::MemoryBuffer &data,
3665 angle::MemoryBuffer &tmp,
3666 const gl::InternalFormat &compressedFormat,
3667 const gl::Extents &extents)
3668 {
3669 constexpr int uncompressedChannelCount = 4;
3670
3671 uint32_t indexBits = 0, redBlueBits = 0, greenBits = 0, alphaBits = 0;
3672 switch (compressedFormat.internalFormat)
3673 {
3674 case GL_PALETTE4_RGB8_OES:
3675 indexBits = 4;
3676 redBlueBits = 8;
3677 greenBits = 8;
3678 alphaBits = 0;
3679 break;
3680 case GL_PALETTE4_RGBA8_OES:
3681 indexBits = 4;
3682 redBlueBits = 8;
3683 greenBits = 8;
3684 alphaBits = 8;
3685 break;
3686 case GL_PALETTE4_R5_G6_B5_OES:
3687 indexBits = 4;
3688 redBlueBits = 5;
3689 greenBits = 6;
3690 alphaBits = 0;
3691 break;
3692 case GL_PALETTE4_RGBA4_OES:
3693 indexBits = 4;
3694 redBlueBits = 4;
3695 greenBits = 4;
3696 alphaBits = 4;
3697 break;
3698 case GL_PALETTE4_RGB5_A1_OES:
3699 indexBits = 4;
3700 redBlueBits = 5;
3701 greenBits = 5;
3702 alphaBits = 1;
3703 break;
3704 case GL_PALETTE8_RGB8_OES:
3705 indexBits = 8;
3706 redBlueBits = 8;
3707 greenBits = 8;
3708 alphaBits = 0;
3709 break;
3710 case GL_PALETTE8_RGBA8_OES:
3711 indexBits = 8;
3712 redBlueBits = 8;
3713 greenBits = 8;
3714 alphaBits = 8;
3715 break;
3716 case GL_PALETTE8_R5_G6_B5_OES:
3717 indexBits = 8;
3718 redBlueBits = 5;
3719 greenBits = 6;
3720 alphaBits = 0;
3721 break;
3722 case GL_PALETTE8_RGBA4_OES:
3723 indexBits = 8;
3724 redBlueBits = 4;
3725 greenBits = 4;
3726 alphaBits = 4;
3727 break;
3728 case GL_PALETTE8_RGB5_A1_OES:
3729 indexBits = 8;
3730 redBlueBits = 5;
3731 greenBits = 5;
3732 alphaBits = 1;
3733 break;
3734
3735 default:
3736 UNREACHABLE();
3737 break;
3738 }
3739
3740 bool result = data.resize(
3741 // Palette size
3742 (1 << indexBits) * (2 * redBlueBits + greenBits + alphaBits) / 8 +
3743 // Texels size
3744 indexBits * extents.width * extents.height * extents.depth / 8);
3745 ASSERT(result);
3746
3747 angle::StoreRGBA8ToPalettedImpl(
3748 extents.width, extents.height, extents.depth, indexBits, redBlueBits, greenBits, alphaBits,
3749 tmp.data(),
3750 uncompressedChannelCount * extents.width, // inputRowPitch
3751 uncompressedChannelCount * extents.width * extents.height, // inputDepthPitch
3752 data.data(), // output
3753 indexBits * extents.width / 8, // outputRowPitch
3754 indexBits * extents.width * extents.height / 8 // outputDepthPitch
3755 );
3756 }
3757
3758 // Capture the setup of the state that's shared by all of the contexts in the share group
3759 // See IsSharedObjectResource for the list of objects covered here.
CaptureShareGroupMidExecutionSetup(gl::Context * context,std::vector<CallCapture> * setupCalls,ResourceTracker * resourceTracker,gl::State & replayState,const PackedEnumMap<ResourceIDType,uint32_t> & maxAccessedResourceIDs)3760 void CaptureShareGroupMidExecutionSetup(
3761 gl::Context *context,
3762 std::vector<CallCapture> *setupCalls,
3763 ResourceTracker *resourceTracker,
3764 gl::State &replayState,
3765 const PackedEnumMap<ResourceIDType, uint32_t> &maxAccessedResourceIDs)
3766 {
3767 FrameCaptureShared *frameCaptureShared = context->getShareGroup()->getFrameCaptureShared();
3768 const gl::State &apiState = context->getState();
3769
3770 // Small helper function to make the code more readable.
3771 auto cap = [setupCalls](CallCapture &&call) { setupCalls->emplace_back(std::move(call)); };
3772
3773 // Capture Buffer data.
3774 const gl::BufferManager &buffers = apiState.getBufferManagerForCapture();
3775 for (const auto &bufferIter : buffers)
3776 {
3777 gl::BufferID id = {bufferIter.first};
3778 gl::Buffer *buffer = bufferIter.second;
3779
3780 if (id.value == 0)
3781 {
3782 continue;
3783 }
3784
3785 // Generate binding.
3786 cap(CaptureGenBuffers(replayState, true, 1, &id));
3787
3788 resourceTracker->getTrackedResource(context->id(), ResourceIDType::Buffer)
3789 .getStartingResources()
3790 .insert(id.value);
3791
3792 MaybeCaptureUpdateResourceIDs(context, resourceTracker, setupCalls);
3793
3794 // glBufferData. Would possibly be better implemented using a getData impl method.
3795 // Saving buffers that are mapped during a swap is not yet handled.
3796 if (buffer->getSize() == 0)
3797 {
3798 resourceTracker->setStartingBufferMapped(buffer->id().value, false);
3799 continue;
3800 }
3801
3802 // Remember if the buffer was already mapped
3803 GLboolean bufferMapped = buffer->isMapped();
3804
3805 // If needed, map the buffer so we can capture its contents
3806 if (!bufferMapped)
3807 {
3808 (void)buffer->mapRange(context, 0, static_cast<GLsizeiptr>(buffer->getSize()),
3809 GL_MAP_READ_BIT);
3810 }
3811
3812 // Always use the array buffer binding point to upload data to keep things simple.
3813 if (buffer != replayState.getArrayBuffer())
3814 {
3815 replayState.setBufferBinding(context, gl::BufferBinding::Array, buffer);
3816 cap(CaptureBindBuffer(replayState, true, gl::BufferBinding::Array, id));
3817 }
3818
3819 if (buffer->isImmutable())
3820 {
3821 cap(CaptureBufferStorageEXT(replayState, true, gl::BufferBinding::Array,
3822 static_cast<GLsizeiptr>(buffer->getSize()),
3823 buffer->getMapPointer(),
3824 buffer->getStorageExtUsageFlags()));
3825 }
3826 else
3827 {
3828 cap(CaptureBufferData(replayState, true, gl::BufferBinding::Array,
3829 static_cast<GLsizeiptr>(buffer->getSize()),
3830 buffer->getMapPointer(), buffer->getUsage()));
3831 }
3832
3833 if (bufferMapped)
3834 {
3835 void *dontCare = nullptr;
3836 CallCapture mapBufferRange =
3837 CaptureMapBufferRange(replayState, true, gl::BufferBinding::Array,
3838 static_cast<GLsizeiptr>(buffer->getMapOffset()),
3839 static_cast<GLsizeiptr>(buffer->getMapLength()),
3840 buffer->getAccessFlags(), dontCare);
3841 CaptureCustomMapBuffer("MapBufferRange", mapBufferRange, *setupCalls, buffer->id());
3842
3843 resourceTracker->setStartingBufferMapped(buffer->id().value, true);
3844
3845 frameCaptureShared->trackBufferMapping(
3846 context, &setupCalls->back(), buffer->id(), buffer,
3847 static_cast<GLsizeiptr>(buffer->getMapOffset()),
3848 static_cast<GLsizeiptr>(buffer->getMapLength()),
3849 (buffer->getAccessFlags() & GL_MAP_WRITE_BIT) != 0,
3850 (buffer->getStorageExtUsageFlags() & GL_MAP_COHERENT_BIT_EXT) != 0);
3851 }
3852 else
3853 {
3854 resourceTracker->setStartingBufferMapped(buffer->id().value, false);
3855 }
3856
3857 // Generate the calls needed to restore this buffer to original state for frame looping
3858 CaptureBufferResetCalls(context, replayState, resourceTracker, &id, buffer);
3859
3860 // Unmap the buffer if it wasn't already mapped
3861 if (!bufferMapped)
3862 {
3863 GLboolean dontCare;
3864 (void)buffer->unmap(context, &dontCare);
3865 }
3866 }
3867
3868 // Clear the array buffer binding.
3869 if (replayState.getTargetBuffer(gl::BufferBinding::Array))
3870 {
3871 cap(CaptureBindBuffer(replayState, true, gl::BufferBinding::Array, {0}));
3872 replayState.setBufferBinding(context, gl::BufferBinding::Array, nullptr);
3873 }
3874
3875 // Set a unpack alignment of 1. Otherwise, computeRowPitch() will compute the wrong value,
3876 // leading to a crash in memcpy() when capturing the texture contents.
3877 gl::PixelUnpackState ¤tUnpackState = replayState.getUnpackState();
3878 if (currentUnpackState.alignment != 1)
3879 {
3880 cap(CapturePixelStorei(replayState, true, GL_UNPACK_ALIGNMENT, 1));
3881 replayState.getMutablePrivateStateForCapture()->setUnpackAlignment(1);
3882 }
3883
3884 // Capture Texture setup and data.
3885 const gl::TextureManager &textures = apiState.getTextureManagerForCapture();
3886
3887 for (const auto &textureIter : textures)
3888 {
3889 gl::TextureID id = {textureIter.first};
3890 gl::Texture *texture = textureIter.second;
3891
3892 if (id.value == 0)
3893 {
3894 continue;
3895 }
3896
3897 // Track this as a starting resource that may need to be restored.
3898 TrackedResource &trackedTextures =
3899 resourceTracker->getTrackedResource(context->id(), ResourceIDType::Texture);
3900 ResourceSet &startingTextures = trackedTextures.getStartingResources();
3901 startingTextures.insert(id.value);
3902
3903 // For the initial texture creation calls, track in the generate list
3904 ResourceCalls &textureRegenCalls = trackedTextures.getResourceRegenCalls();
3905 CallVector texGenCalls({setupCalls, &textureRegenCalls[id.value]});
3906
3907 // Gen the Texture.
3908 for (std::vector<CallCapture> *calls : texGenCalls)
3909 {
3910 Capture(calls, CaptureGenTextures(replayState, true, 1, &id));
3911 MaybeCaptureUpdateResourceIDs(context, resourceTracker, calls);
3912 }
3913
3914 // For the remaining texture setup calls, track in the restore list
3915 ResourceCalls &textureRestoreCalls = trackedTextures.getResourceRestoreCalls();
3916 CallVector texSetupCalls({setupCalls, &textureRestoreCalls[id.value]});
3917
3918 for (std::vector<CallCapture> *calls : texSetupCalls)
3919 {
3920 Capture(calls, CaptureBindTexture(replayState, true, texture->getType(), id));
3921 }
3922 replayState.setSamplerTexture(context, texture->getType(), texture);
3923
3924 // Capture sampler parameter states.
3925 // TODO(jmadill): More sampler / texture states. http://anglebug.com/3662
3926 gl::SamplerState defaultSamplerState =
3927 gl::SamplerState::CreateDefaultForTarget(texture->getType());
3928 const gl::SamplerState &textureSamplerState = texture->getSamplerState();
3929
3930 auto capTexParam = [&replayState, texture, &texSetupCalls](GLenum pname, GLint param) {
3931 for (std::vector<CallCapture> *calls : texSetupCalls)
3932 {
3933 Capture(calls,
3934 CaptureTexParameteri(replayState, true, texture->getType(), pname, param));
3935 }
3936 };
3937
3938 auto capTexParamf = [&replayState, texture, &texSetupCalls](GLenum pname, GLfloat param) {
3939 for (std::vector<CallCapture> *calls : texSetupCalls)
3940 {
3941 Capture(calls,
3942 CaptureTexParameterf(replayState, true, texture->getType(), pname, param));
3943 }
3944 };
3945
3946 if (textureSamplerState.getMinFilter() != defaultSamplerState.getMinFilter())
3947 {
3948 capTexParam(GL_TEXTURE_MIN_FILTER, textureSamplerState.getMinFilter());
3949 }
3950
3951 if (textureSamplerState.getMagFilter() != defaultSamplerState.getMagFilter())
3952 {
3953 capTexParam(GL_TEXTURE_MAG_FILTER, textureSamplerState.getMagFilter());
3954 }
3955
3956 if (textureSamplerState.getWrapR() != defaultSamplerState.getWrapR())
3957 {
3958 capTexParam(GL_TEXTURE_WRAP_R, textureSamplerState.getWrapR());
3959 }
3960
3961 if (textureSamplerState.getWrapS() != defaultSamplerState.getWrapS())
3962 {
3963 capTexParam(GL_TEXTURE_WRAP_S, textureSamplerState.getWrapS());
3964 }
3965
3966 if (textureSamplerState.getWrapT() != defaultSamplerState.getWrapT())
3967 {
3968 capTexParam(GL_TEXTURE_WRAP_T, textureSamplerState.getWrapT());
3969 }
3970
3971 if (textureSamplerState.getMinLod() != defaultSamplerState.getMinLod())
3972 {
3973 capTexParamf(GL_TEXTURE_MIN_LOD, textureSamplerState.getMinLod());
3974 }
3975
3976 if (textureSamplerState.getMaxLod() != defaultSamplerState.getMaxLod())
3977 {
3978 capTexParamf(GL_TEXTURE_MAX_LOD, textureSamplerState.getMaxLod());
3979 }
3980
3981 if (textureSamplerState.getCompareMode() != defaultSamplerState.getCompareMode())
3982 {
3983 capTexParam(GL_TEXTURE_COMPARE_MODE, textureSamplerState.getCompareMode());
3984 }
3985
3986 if (textureSamplerState.getCompareFunc() != defaultSamplerState.getCompareFunc())
3987 {
3988 capTexParam(GL_TEXTURE_COMPARE_FUNC, textureSamplerState.getCompareFunc());
3989 }
3990
3991 // Texture parameters
3992 if (texture->getSwizzleRed() != GL_RED)
3993 {
3994 capTexParam(GL_TEXTURE_SWIZZLE_R, texture->getSwizzleRed());
3995 }
3996
3997 if (texture->getSwizzleGreen() != GL_GREEN)
3998 {
3999 capTexParam(GL_TEXTURE_SWIZZLE_G, texture->getSwizzleGreen());
4000 }
4001
4002 if (texture->getSwizzleBlue() != GL_BLUE)
4003 {
4004 capTexParam(GL_TEXTURE_SWIZZLE_B, texture->getSwizzleBlue());
4005 }
4006
4007 if (texture->getSwizzleAlpha() != GL_ALPHA)
4008 {
4009 capTexParam(GL_TEXTURE_SWIZZLE_A, texture->getSwizzleAlpha());
4010 }
4011
4012 if (texture->getBaseLevel() != 0)
4013 {
4014 capTexParam(GL_TEXTURE_BASE_LEVEL, texture->getBaseLevel());
4015 }
4016
4017 if (texture->getMaxLevel() != 1000)
4018 {
4019 capTexParam(GL_TEXTURE_MAX_LEVEL, texture->getMaxLevel());
4020 }
4021
4022 // If the texture is immutable, initialize it with TexStorage
4023 if (texture->getImmutableFormat())
4024 {
4025 // We can only call TexStorage *once* on an immutable texture, so it needs special
4026 // handling. To solve this, immutable textures will have a BindTexture and TexStorage as
4027 // part of their textureRegenCalls. The resulting regen sequence will be:
4028 //
4029 // const GLuint glDeleteTextures_texturesPacked_0[] = { gTextureMap[52] };
4030 // glDeleteTextures(1, glDeleteTextures_texturesPacked_0);
4031 // glGenTextures(1, reinterpret_cast<GLuint *>(gReadBuffer));
4032 // UpdateTextureID(52, 0);
4033 // glBindTexture(GL_TEXTURE_2D, gTextureMap[52]);
4034 // glTexStorage2D(GL_TEXTURE_2D, 1, GL_R8, 256, 512);
4035
4036 // Bind the texture first just for textureRegenCalls
4037 Capture(&textureRegenCalls[id.value],
4038 CaptureBindTexture(replayState, true, texture->getType(), id));
4039
4040 // Then add TexStorage to texGenCalls instead of texSetupCalls
4041 for (std::vector<CallCapture> *calls : texGenCalls)
4042 {
4043 CaptureTextureStorage(calls, &replayState, texture);
4044 }
4045 }
4046
4047 // Iterate texture levels and layers.
4048 gl::ImageIndexIterator imageIter = gl::ImageIndexIterator::MakeGeneric(
4049 texture->getType(), 0, texture->getMipmapMaxLevel() + 1, gl::ImageIndex::kEntireLevel,
4050 gl::ImageIndex::kEntireLevel);
4051 while (imageIter.hasNext())
4052 {
4053 gl::ImageIndex index = imageIter.next();
4054
4055 const gl::ImageDesc &desc = texture->getTextureState().getImageDesc(index);
4056
4057 if (desc.size.empty())
4058 {
4059 continue;
4060 }
4061
4062 const gl::InternalFormat &format = *desc.format.info;
4063
4064 bool supportedType = (index.getType() == gl::TextureType::_2D ||
4065 index.getType() == gl::TextureType::_3D ||
4066 index.getType() == gl::TextureType::_2DArray ||
4067 index.getType() == gl::TextureType::Buffer ||
4068 index.getType() == gl::TextureType::CubeMap ||
4069 index.getType() == gl::TextureType::CubeMapArray ||
4070 index.getType() == gl::TextureType::External);
4071
4072 // Check for supported textures
4073 if (!supportedType)
4074 {
4075 ERR() << "Unsupported texture type: " << index.getType();
4076 UNREACHABLE();
4077 }
4078
4079 if (index.getType() == gl::TextureType::Buffer)
4080 {
4081 // The buffer contents are already backed up, but we need to emit the TexBuffer
4082 // binding calls
4083 for (std::vector<CallCapture> *calls : texSetupCalls)
4084 {
4085 CaptureTextureContents(calls, &replayState, texture, index, desc, 0, 0);
4086 }
4087 continue;
4088 }
4089
4090 // create a staging GL_TEXTURE_2D texture to create the eglImage with
4091 gl::TextureID stagingTexId = {maxAccessedResourceIDs[ResourceIDType::Texture] + 1};
4092 if (index.getType() == gl::TextureType::External)
4093 {
4094 Capture(setupCalls, CaptureGenTextures(replayState, true, 1, &stagingTexId));
4095 MaybeCaptureUpdateResourceIDs(context, resourceTracker, setupCalls);
4096 Capture(setupCalls,
4097 CaptureBindTexture(replayState, true, gl::TextureType::_2D, stagingTexId));
4098 Capture(setupCalls, CaptureTexParameteri(replayState, true, gl::TextureType::_2D,
4099 GL_TEXTURE_MIN_FILTER, GL_NEAREST));
4100 Capture(setupCalls, CaptureTexParameteri(replayState, true, gl::TextureType::_2D,
4101 GL_TEXTURE_MAG_FILTER, GL_NEAREST));
4102 }
4103
4104 if (context->getExtensions().getImageANGLE)
4105 {
4106 // Use ANGLE_get_image to read back pixel data.
4107 angle::MemoryBuffer data;
4108
4109 const gl::Extents extents(desc.size.width, desc.size.height, desc.size.depth);
4110
4111 gl::PixelPackState packState;
4112 packState.alignment = 1;
4113
4114 if (format.paletted)
4115 {
4116 // Read back the uncompressed texture, then re-compress it
4117 // to store in the trace.
4118
4119 angle::MemoryBuffer tmp;
4120
4121 // The uncompressed format (R8G8B8A8) is 4 bytes per texel
4122 bool result = tmp.resize(4 * extents.width * extents.height * extents.depth);
4123 ASSERT(result);
4124
4125 (void)texture->getTexImage(context, packState, nullptr, index.getTarget(),
4126 index.getLevelIndex(), GL_RGBA, GL_UNSIGNED_BYTE,
4127 tmp.data());
4128
4129 CompressPalettedTexture(data, tmp, format, extents);
4130 }
4131 else if (format.compressed)
4132 {
4133 // Calculate the size needed to store the compressed level
4134 GLuint sizeInBytes;
4135 bool result = format.computeCompressedImageSize(extents, &sizeInBytes);
4136 ASSERT(result);
4137
4138 result = data.resize(sizeInBytes);
4139 ASSERT(result);
4140
4141 (void)texture->getCompressedTexImage(context, packState, nullptr,
4142 index.getTarget(), index.getLevelIndex(),
4143 data.data());
4144 }
4145 else
4146 {
4147 GLenum getFormat = format.format;
4148 GLenum getType = format.type;
4149
4150 const gl::PixelUnpackState &unpack = apiState.getUnpackState();
4151
4152 GLuint endByte = 0;
4153 bool unpackSize =
4154 format.computePackUnpackEndByte(getType, extents, unpack, true, &endByte);
4155 ASSERT(unpackSize);
4156
4157 bool result = data.resize(endByte);
4158 ASSERT(result);
4159
4160 (void)texture->getTexImage(context, packState, nullptr, index.getTarget(),
4161 index.getLevelIndex(), getFormat, getType,
4162 data.data());
4163 }
4164
4165 for (std::vector<CallCapture> *calls : texSetupCalls)
4166 {
4167 CaptureTextureContents(calls, &replayState, texture, index, desc,
4168 static_cast<GLuint>(data.size()), data.data());
4169 }
4170
4171 if (index.getType() == gl::TextureType::External)
4172 {
4173 // Look up the attribs used when the image was created
4174 // Firstly, lookup the eglImage ID associated with this texture when the app
4175 // issued glEGLImageTargetTexture2DOES()
4176 auto eglImageIter = resourceTracker->getTextureIDToImageTable().find(id.value);
4177 ASSERT(eglImageIter != resourceTracker->getTextureIDToImageTable().end());
4178
4179 const egl::ImageID eglImageID = eglImageIter->second;
4180 const EGLImage eglImage =
4181 reinterpret_cast<EGLImage>(static_cast<uintptr_t>(eglImageID.value));
4182
4183 // Secondly, lookup the attrib we used to create the eglImage
4184 auto eglImageAttribIter =
4185 resourceTracker->getImageToAttribTable().find(eglImage);
4186 ASSERT(eglImageAttribIter != resourceTracker->getImageToAttribTable().end());
4187
4188 const egl::AttributeMap &retrievedAttribs = eglImageAttribIter->second;
4189
4190 // Create the image on demand with the same attrib retrieved above
4191 CallCapture eglCreateImageKHRCall = egl::CaptureCreateImageKHR(
4192 nullptr, true, nullptr, context->id(), EGL_GL_TEXTURE_2D_KHR,
4193 reinterpret_cast<EGLClientBuffer>(
4194 static_cast<GLuint64>(stagingTexId.value)),
4195 retrievedAttribs, eglImage);
4196
4197 // Convert the CaptureCreateImageKHR CallCapture to the customized CallCapture
4198 std::vector<CallCapture> eglCustomCreateImageKHRCall;
4199 CaptureCustomCreateEGLImage("CreateEGLImageKHR", eglCreateImageKHRCall,
4200 eglCustomCreateImageKHRCall);
4201 ASSERT(eglCustomCreateImageKHRCall.size() > 0);
4202
4203 // Append the customized CallCapture to the setupCalls list
4204 Capture(setupCalls, std::move(eglCustomCreateImageKHRCall[0]));
4205
4206 // Pass the eglImage to the texture that is bound to GL_TEXTURE_EXTERNAL_OES
4207 // target
4208 for (std::vector<CallCapture> *calls : texSetupCalls)
4209 {
4210 Capture(calls,
4211 CaptureEGLImageTargetTexture2DOES(
4212 replayState, true, gl::TextureType::External, eglImageID));
4213 }
4214
4215 // Delete the staging texture
4216 Capture(setupCalls, CaptureDeleteTextures(replayState, true, 1, &stagingTexId));
4217 }
4218 }
4219 else
4220 {
4221 for (std::vector<CallCapture> *calls : texSetupCalls)
4222 {
4223 CaptureTextureContents(calls, &replayState, texture, index, desc, 0, nullptr);
4224 }
4225 }
4226 }
4227 }
4228
4229 // Capture Renderbuffers.
4230 const gl::RenderbufferManager &renderbuffers = apiState.getRenderbufferManagerForCapture();
4231 FramebufferCaptureFuncs framebufferFuncs(context->isGLES1());
4232
4233 for (const auto &renderbufIter : renderbuffers)
4234 {
4235 gl::RenderbufferID id = {renderbufIter.first};
4236 const gl::Renderbuffer *renderbuffer = renderbufIter.second;
4237
4238 // Track this as a starting resource that may need to be restored.
4239 TrackedResource &trackedRenderbuffers =
4240 resourceTracker->getTrackedResource(context->id(), ResourceIDType::Renderbuffer);
4241 ResourceSet &startingRenderbuffers = trackedRenderbuffers.getStartingResources();
4242 startingRenderbuffers.insert(id.value);
4243
4244 // For the initial renderbuffer creation calls, track in the generate list
4245 ResourceCalls &renderbufferRegenCalls = trackedRenderbuffers.getResourceRegenCalls();
4246 CallVector rbGenCalls({setupCalls, &renderbufferRegenCalls[id.value]});
4247
4248 // Generate renderbuffer id.
4249 for (std::vector<CallCapture> *calls : rbGenCalls)
4250 {
4251 Capture(calls, framebufferFuncs.genRenderbuffers(replayState, true, 1, &id));
4252 MaybeCaptureUpdateResourceIDs(context, resourceTracker, calls);
4253 Capture(calls,
4254 framebufferFuncs.bindRenderbuffer(replayState, true, GL_RENDERBUFFER, id));
4255 }
4256
4257 GLenum internalformat = renderbuffer->getFormat().info->internalFormat;
4258
4259 if (renderbuffer->getSamples() > 0)
4260 {
4261 // Note: We could also use extensions if available.
4262 for (std::vector<CallCapture> *calls : rbGenCalls)
4263 {
4264 Capture(calls,
4265 CaptureRenderbufferStorageMultisample(
4266 replayState, true, GL_RENDERBUFFER, renderbuffer->getSamples(),
4267 internalformat, renderbuffer->getWidth(), renderbuffer->getHeight()));
4268 }
4269 }
4270 else
4271 {
4272 for (std::vector<CallCapture> *calls : rbGenCalls)
4273 {
4274 Capture(calls, framebufferFuncs.renderbufferStorage(
4275 replayState, true, GL_RENDERBUFFER, internalformat,
4276 renderbuffer->getWidth(), renderbuffer->getHeight()));
4277 }
4278 }
4279
4280 // TODO: Capture renderbuffer contents. http://anglebug.com/3662
4281 }
4282
4283 // Capture Shaders and Programs.
4284 const gl::ShaderProgramManager &shadersAndPrograms =
4285 apiState.getShaderProgramManagerForCapture();
4286 const gl::ResourceMap<gl::Shader, gl::ShaderProgramID> &shaders =
4287 shadersAndPrograms.getShadersForCapture();
4288 const gl::ResourceMap<gl::Program, gl::ShaderProgramID> &programs =
4289 shadersAndPrograms.getProgramsForCaptureAndPerf();
4290
4291 TrackedResource &trackedShaderPrograms =
4292 resourceTracker->getTrackedResource(context->id(), ResourceIDType::ShaderProgram);
4293
4294 // Capture Program binary state.
4295 gl::ShaderProgramID tempShaderStartID = {resourceTracker->getMaxShaderPrograms()};
4296 for (const auto &programIter : programs)
4297 {
4298 gl::ShaderProgramID id = {programIter.first};
4299 gl::Program *program = programIter.second;
4300
4301 // Unlinked programs don't have an executable. Thus they don't need to be captured.
4302 // Programs are shared by contexts in the share group and only need to be captured once.
4303 if (!program->isLinked())
4304 {
4305 continue;
4306 }
4307
4308 size_t programSetupStart = setupCalls->size();
4309
4310 // Get last linked shader source.
4311 const ProgramSources &linkedSources =
4312 context->getShareGroup()->getFrameCaptureShared()->getProgramSources(id);
4313
4314 // Create two lists for program regen calls
4315 ResourceCalls &shaderProgramRegenCalls = trackedShaderPrograms.getResourceRegenCalls();
4316 CallVector programRegenCalls({setupCalls, &shaderProgramRegenCalls[id.value]});
4317
4318 for (std::vector<CallCapture> *calls : programRegenCalls)
4319 {
4320 CallCapture createProgram = CaptureCreateProgram(replayState, true, id.value);
4321 CaptureCustomShaderProgram("CreateProgram", createProgram, *calls);
4322 }
4323
4324 // Create two lists for program restore calls
4325 ResourceCalls &shaderProgramRestoreCalls = trackedShaderPrograms.getResourceRestoreCalls();
4326 CallVector programRestoreCalls({setupCalls, &shaderProgramRestoreCalls[id.value]});
4327
4328 for (std::vector<CallCapture> *calls : programRestoreCalls)
4329 {
4330 GenerateLinkedProgram(context, replayState, resourceTracker, calls, program, id,
4331 tempShaderStartID, linkedSources);
4332 }
4333
4334 // Update the program in replayState
4335 if (!replayState.getProgram() || replayState.getProgram()->id() != program->id())
4336 {
4337 // Note: We don't do this in GenerateLinkedProgram because it can't modify state
4338 (void)replayState.setProgram(context, program);
4339 }
4340
4341 resourceTracker->getTrackedResource(context->id(), ResourceIDType::ShaderProgram)
4342 .getStartingResources()
4343 .insert(id.value);
4344 resourceTracker->setShaderProgramType(id, ShaderProgramType::ProgramType);
4345
4346 size_t programSetupEnd = setupCalls->size();
4347
4348 // Mark the range of calls used to setup this program
4349 frameCaptureShared->markResourceSetupCallsInactive(
4350 setupCalls, ResourceIDType::ShaderProgram, id.value,
4351 gl::Range<size_t>(programSetupStart, programSetupEnd));
4352 }
4353
4354 // Handle shaders.
4355 for (const auto &shaderIter : shaders)
4356 {
4357 gl::ShaderProgramID id = {shaderIter.first};
4358 gl::Shader *shader = shaderIter.second;
4359
4360 // Skip shaders scheduled for deletion.
4361 // Shaders are shared by contexts in the share group and only need to be captured once.
4362 if (shader->hasBeenDeleted())
4363 {
4364 continue;
4365 }
4366
4367 size_t shaderSetupStart = setupCalls->size();
4368
4369 // Create two lists for shader regen calls
4370 ResourceCalls &shaderProgramRegenCalls = trackedShaderPrograms.getResourceRegenCalls();
4371 CallVector shaderRegenCalls({setupCalls, &shaderProgramRegenCalls[id.value]});
4372
4373 for (std::vector<CallCapture> *calls : shaderRegenCalls)
4374 {
4375 CallCapture createShader =
4376 CaptureCreateShader(replayState, true, shader->getType(), id.value);
4377 CaptureCustomShaderProgram("CreateShader", createShader, *calls);
4378 }
4379
4380 std::string shaderSource = shader->getSourceString();
4381 const char *sourcePointer = shaderSource.empty() ? nullptr : shaderSource.c_str();
4382
4383 // Create two lists for shader restore calls
4384 ResourceCalls &shaderProgramRestoreCalls = trackedShaderPrograms.getResourceRestoreCalls();
4385 CallVector shaderRestoreCalls({setupCalls, &shaderProgramRestoreCalls[id.value]});
4386
4387 // This does not handle some more tricky situations like attaching shaders to a non-linked
4388 // program. Or attaching uncompiled shaders. Or attaching and then deleting a shader.
4389 // TODO(jmadill): Handle trickier program uses. http://anglebug.com/3662
4390 if (shader->isCompiled(context))
4391 {
4392 const std::string &capturedSource =
4393 context->getShareGroup()->getFrameCaptureShared()->getShaderSource(id);
4394 if (capturedSource != shaderSource)
4395 {
4396 ASSERT(!capturedSource.empty());
4397 sourcePointer = capturedSource.c_str();
4398 }
4399
4400 for (std::vector<CallCapture> *calls : shaderRestoreCalls)
4401 {
4402 Capture(calls,
4403 CaptureShaderSource(replayState, true, id, 1, &sourcePointer, nullptr));
4404 Capture(calls, CaptureCompileShader(replayState, true, id));
4405 }
4406 }
4407
4408 if (sourcePointer &&
4409 (!shader->isCompiled(context) || sourcePointer != shaderSource.c_str()))
4410 {
4411 for (std::vector<CallCapture> *calls : shaderRestoreCalls)
4412 {
4413 Capture(calls,
4414 CaptureShaderSource(replayState, true, id, 1, &sourcePointer, nullptr));
4415 }
4416 }
4417
4418 size_t shaderSetupEnd = setupCalls->size();
4419
4420 // Mark the range of calls used to setup this shader
4421 frameCaptureShared->markResourceSetupCallsInactive(
4422 setupCalls, ResourceIDType::ShaderProgram, id.value,
4423 gl::Range<size_t>(shaderSetupStart, shaderSetupEnd));
4424
4425 resourceTracker->getTrackedResource(context->id(), ResourceIDType::ShaderProgram)
4426 .getStartingResources()
4427 .insert(id.value);
4428 resourceTracker->setShaderProgramType(id, ShaderProgramType::ShaderType);
4429 }
4430
4431 // Capture Sampler Objects
4432 const gl::SamplerManager &samplers = apiState.getSamplerManagerForCapture();
4433 for (const auto &samplerIter : samplers)
4434 {
4435 gl::SamplerID samplerID = {samplerIter.first};
4436
4437 // Don't gen the sampler if we've seen it before, since they are shared across the context
4438 // share group.
4439 cap(CaptureGenSamplers(replayState, true, 1, &samplerID));
4440 MaybeCaptureUpdateResourceIDs(context, resourceTracker, setupCalls);
4441
4442 gl::Sampler *sampler = samplerIter.second;
4443 if (!sampler)
4444 {
4445 continue;
4446 }
4447
4448 gl::SamplerState defaultSamplerState;
4449 if (sampler->getMinFilter() != defaultSamplerState.getMinFilter())
4450 {
4451 cap(CaptureSamplerParameteri(replayState, true, samplerID, GL_TEXTURE_MIN_FILTER,
4452 sampler->getMinFilter()));
4453 }
4454 if (sampler->getMagFilter() != defaultSamplerState.getMagFilter())
4455 {
4456 cap(CaptureSamplerParameteri(replayState, true, samplerID, GL_TEXTURE_MAG_FILTER,
4457 sampler->getMagFilter()));
4458 }
4459 if (sampler->getWrapS() != defaultSamplerState.getWrapS())
4460 {
4461 cap(CaptureSamplerParameteri(replayState, true, samplerID, GL_TEXTURE_WRAP_S,
4462 sampler->getWrapS()));
4463 }
4464 if (sampler->getWrapR() != defaultSamplerState.getWrapR())
4465 {
4466 cap(CaptureSamplerParameteri(replayState, true, samplerID, GL_TEXTURE_WRAP_R,
4467 sampler->getWrapR()));
4468 }
4469 if (sampler->getWrapT() != defaultSamplerState.getWrapT())
4470 {
4471 cap(CaptureSamplerParameteri(replayState, true, samplerID, GL_TEXTURE_WRAP_T,
4472 sampler->getWrapT()));
4473 }
4474 if (sampler->getMinLod() != defaultSamplerState.getMinLod())
4475 {
4476 cap(CaptureSamplerParameterf(replayState, true, samplerID, GL_TEXTURE_MIN_LOD,
4477 sampler->getMinLod()));
4478 }
4479 if (sampler->getMaxLod() != defaultSamplerState.getMaxLod())
4480 {
4481 cap(CaptureSamplerParameterf(replayState, true, samplerID, GL_TEXTURE_MAX_LOD,
4482 sampler->getMaxLod()));
4483 }
4484 if (sampler->getCompareMode() != defaultSamplerState.getCompareMode())
4485 {
4486 cap(CaptureSamplerParameteri(replayState, true, samplerID, GL_TEXTURE_COMPARE_MODE,
4487 sampler->getCompareMode()));
4488 }
4489 if (sampler->getCompareFunc() != defaultSamplerState.getCompareFunc())
4490 {
4491 cap(CaptureSamplerParameteri(replayState, true, samplerID, GL_TEXTURE_COMPARE_FUNC,
4492 sampler->getCompareFunc()));
4493 }
4494 }
4495
4496 // Capture Sync Objects
4497 const gl::SyncManager &syncs = apiState.getSyncManagerForCapture();
4498 for (const auto &syncIter : syncs)
4499 {
4500 gl::SyncID syncID = {syncIter.first};
4501 const gl::Sync *sync = syncIter.second;
4502 GLsync syncObject = gl::unsafe_int_to_pointer_cast<GLsync>(syncID.value);
4503
4504 if (!sync)
4505 {
4506 continue;
4507 }
4508 CallCapture fenceSync =
4509 CaptureFenceSync(replayState, true, sync->getCondition(), sync->getFlags(), syncObject);
4510 CaptureCustomFenceSync(fenceSync, *setupCalls);
4511 CaptureFenceSyncResetCalls(context, replayState, resourceTracker, syncID, syncObject, sync);
4512 resourceTracker->getStartingFenceSyncs().insert(syncID);
4513 }
4514
4515 // Capture EGL Sync Objects
4516 const egl::SyncMap eglSyncMap = context->getDisplay()->getSyncsForCapture();
4517 for (const auto &eglSyncIter : eglSyncMap)
4518 {
4519 egl::SyncID eglSyncID = {eglSyncIter.first};
4520 const egl::Sync *eglSync = eglSyncIter.second;
4521 EGLSync eglSyncObject = gl::unsafe_int_to_pointer_cast<EGLSync>(eglSyncID.value);
4522
4523 if (!eglSync)
4524 {
4525 continue;
4526 }
4527 CallCapture createEGLSync =
4528 CaptureCreateSync(nullptr, true, context->getDisplay(), eglSync->getType(),
4529 eglSync->getAttributeMap(), eglSyncObject);
4530 CaptureCustomCreateEGLSync("CreateEGLSyncKHR", createEGLSync, *setupCalls);
4531 CaptureEGLSyncResetCalls(context, replayState, resourceTracker, eglSyncID, eglSyncObject,
4532 eglSync);
4533 resourceTracker->getTrackedResource(context->id(), ResourceIDType::egl_Sync)
4534 .getStartingResources()
4535 .insert(eglSyncID.value);
4536 }
4537
4538 GLint contextUnpackAlignment = context->getState().getUnpackState().alignment;
4539 if (currentUnpackState.alignment != contextUnpackAlignment)
4540 {
4541 cap(CapturePixelStorei(replayState, true, GL_UNPACK_ALIGNMENT, contextUnpackAlignment));
4542 replayState.getMutablePrivateStateForCapture()->setUnpackAlignment(contextUnpackAlignment);
4543 }
4544 }
4545
CaptureMidExecutionSetup(const gl::Context * context,std::vector<CallCapture> * setupCalls,CallResetMap & resetCalls,std::vector<CallCapture> * shareGroupSetupCalls,ResourceIDToSetupCallsMap * resourceIDToSetupCalls,ResourceTracker * resourceTracker,gl::State & replayState,bool validationEnabled)4546 void CaptureMidExecutionSetup(const gl::Context *context,
4547 std::vector<CallCapture> *setupCalls,
4548 CallResetMap &resetCalls,
4549 std::vector<CallCapture> *shareGroupSetupCalls,
4550 ResourceIDToSetupCallsMap *resourceIDToSetupCalls,
4551 ResourceTracker *resourceTracker,
4552 gl::State &replayState,
4553 bool validationEnabled)
4554 {
4555 const gl::State &apiState = context->getState();
4556
4557 // Small helper function to make the code more readable.
4558 auto cap = [setupCalls](CallCapture &&call) { setupCalls->emplace_back(std::move(call)); };
4559
4560 cap(egl::CaptureMakeCurrent(nullptr, true, nullptr, {0}, {0}, context->id(), EGL_TRUE));
4561
4562 // Vertex input states. Must happen after buffer data initialization. Do not capture on GLES1.
4563 if (!context->isGLES1())
4564 {
4565 CaptureDefaultVertexAttribs(replayState, apiState, setupCalls);
4566 }
4567
4568 // Capture vertex array objects
4569 VertexArrayCaptureFuncs vertexArrayFuncs(context->isGLES1());
4570
4571 const gl::VertexArrayMap &vertexArrayMap = context->getVertexArraysForCapture();
4572 gl::VertexArrayID boundVertexArrayID = {0};
4573 for (const auto &vertexArrayIter : vertexArrayMap)
4574 {
4575 TrackedResource &trackedVertexArrays =
4576 resourceTracker->getTrackedResource(context->id(), ResourceIDType::VertexArray);
4577
4578 gl::VertexArrayID vertexArrayID = {vertexArrayIter.first};
4579
4580 // Track this as a starting resource that may need to be restored
4581 resourceTracker->getTrackedResource(context->id(), ResourceIDType::VertexArray)
4582 .getStartingResources()
4583 .insert(vertexArrayID.value);
4584
4585 // Create two lists of calls for initial setup
4586 ResourceCalls &vertexArrayRegenCalls = trackedVertexArrays.getResourceRegenCalls();
4587 CallVector vertexArrayGenCalls({setupCalls, &vertexArrayRegenCalls[vertexArrayID.value]});
4588
4589 if (vertexArrayID.value != 0)
4590 {
4591 // Gen the vertex array
4592 for (std::vector<CallCapture> *calls : vertexArrayGenCalls)
4593 {
4594 Capture(calls,
4595 vertexArrayFuncs.genVertexArrays(replayState, true, 1, &vertexArrayID));
4596 MaybeCaptureUpdateResourceIDs(context, resourceTracker, calls);
4597 }
4598 }
4599
4600 // Create two lists of calls for populating the vertex array
4601 ResourceCalls &vertexArrayRestoreCalls = trackedVertexArrays.getResourceRestoreCalls();
4602 CallVector vertexArraySetupCalls(
4603 {setupCalls, &vertexArrayRestoreCalls[vertexArrayID.value]});
4604
4605 if (vertexArrayIter.second)
4606 {
4607 const gl::VertexArray *vertexArray = vertexArrayIter.second;
4608
4609 // Populate the vertex array
4610 for (std::vector<CallCapture> *calls : vertexArraySetupCalls)
4611 {
4612 // Bind the vertexArray (if needed) and populate it
4613 if (vertexArrayID != boundVertexArrayID)
4614 {
4615 Capture(calls,
4616 vertexArrayFuncs.bindVertexArray(replayState, true, vertexArrayID));
4617 }
4618 CaptureVertexArrayState(calls, context, vertexArray, &replayState);
4619 }
4620 boundVertexArrayID = vertexArrayID;
4621 }
4622 }
4623
4624 // Bind the current vertex array
4625 const gl::VertexArray *currentVertexArray = apiState.getVertexArray();
4626 if (currentVertexArray->id() != boundVertexArrayID)
4627 {
4628 cap(vertexArrayFuncs.bindVertexArray(replayState, true, currentVertexArray->id()));
4629 }
4630
4631 // Track the calls necessary to bind the vertex array back to initial state
4632 Capture(&resetCalls[angle::EntryPoint::GLBindVertexArray],
4633 vertexArrayFuncs.bindVertexArray(replayState, true, currentVertexArray->id()));
4634
4635 // Capture indexed buffer bindings.
4636 const gl::BufferVector &uniformIndexedBuffers =
4637 apiState.getOffsetBindingPointerUniformBuffers();
4638 const gl::BufferVector &atomicCounterIndexedBuffers =
4639 apiState.getOffsetBindingPointerAtomicCounterBuffers();
4640 const gl::BufferVector &shaderStorageIndexedBuffers =
4641 apiState.getOffsetBindingPointerShaderStorageBuffers();
4642 CaptureIndexedBuffers(replayState, uniformIndexedBuffers, gl::BufferBinding::Uniform,
4643 setupCalls);
4644 CaptureIndexedBuffers(replayState, atomicCounterIndexedBuffers,
4645 gl::BufferBinding::AtomicCounter, setupCalls);
4646 CaptureIndexedBuffers(replayState, shaderStorageIndexedBuffers,
4647 gl::BufferBinding::ShaderStorage, setupCalls);
4648
4649 // Capture Buffer bindings.
4650 const gl::BoundBufferMap &boundBuffers = apiState.getBoundBuffersForCapture();
4651 for (gl::BufferBinding binding : angle::AllEnums<gl::BufferBinding>())
4652 {
4653 gl::BufferID bufferID = boundBuffers[binding].id();
4654
4655 // Filter out redundant buffer binding commands. Note that the code in the previous section
4656 // only binds to ARRAY_BUFFER. Therefore we only check the array binding against the binding
4657 // we set earlier.
4658 bool isArray = binding == gl::BufferBinding::Array;
4659 const gl::Buffer *arrayBuffer = replayState.getArrayBuffer();
4660 if ((isArray && arrayBuffer && arrayBuffer->id() != bufferID) ||
4661 (!isArray && bufferID.value != 0))
4662 {
4663 cap(CaptureBindBuffer(replayState, true, binding, bufferID));
4664 replayState.setBufferBinding(context, binding, boundBuffers[binding].get());
4665 }
4666
4667 // Restore all buffer bindings for Reset
4668 if (bufferID.value != 0)
4669 {
4670 CaptureBufferBindingResetCalls(replayState, resourceTracker, binding, bufferID);
4671 }
4672 }
4673
4674 // Set a unpack alignment of 1. Otherwise, computeRowPitch() will compute the wrong value,
4675 // leading to a crash in memcpy() when capturing the texture contents.
4676 gl::PixelUnpackState ¤tUnpackState = replayState.getUnpackState();
4677 if (currentUnpackState.alignment != 1)
4678 {
4679 cap(CapturePixelStorei(replayState, true, GL_UNPACK_ALIGNMENT, 1));
4680 replayState.getMutablePrivateStateForCapture()->setUnpackAlignment(1);
4681 }
4682
4683 // Capture Texture setup and data.
4684 const gl::TextureBindingMap &apiBoundTextures = apiState.getBoundTexturesForCapture();
4685
4686 // Set Texture bindings.
4687 for (gl::TextureType textureType : angle::AllEnums<gl::TextureType>())
4688 {
4689 const gl::TextureBindingVector &apiBindings = apiBoundTextures[textureType];
4690 const gl::TextureBindingVector &replayBindings =
4691 replayState.getBoundTexturesForCapture()[textureType];
4692 ASSERT(apiBindings.size() == replayBindings.size());
4693 for (size_t bindingIndex = 0; bindingIndex < apiBindings.size(); ++bindingIndex)
4694 {
4695 gl::TextureID apiTextureID = apiBindings[bindingIndex].id();
4696 gl::TextureID replayTextureID = replayBindings[bindingIndex].id();
4697
4698 if (apiTextureID != replayTextureID)
4699 {
4700 if (replayState.getActiveSampler() != bindingIndex)
4701 {
4702 cap(CaptureActiveTexture(replayState, true,
4703 GL_TEXTURE0 + static_cast<GLenum>(bindingIndex)));
4704 replayState.getMutablePrivateStateForCapture()->setActiveSampler(
4705 static_cast<unsigned int>(bindingIndex));
4706 }
4707
4708 cap(CaptureBindTexture(replayState, true, textureType, apiTextureID));
4709 replayState.setSamplerTexture(context, textureType,
4710 apiBindings[bindingIndex].get());
4711 }
4712 }
4713 }
4714
4715 // Capture Texture Environment
4716 if (context->isGLES1())
4717 {
4718 const gl::Caps &caps = context->getCaps();
4719 for (GLuint unit = 0; unit < caps.maxMultitextureUnits; ++unit)
4720 {
4721 CaptureTextureEnvironmentState(setupCalls, &replayState, &apiState, unit);
4722 }
4723 }
4724
4725 // Set active Texture.
4726 if (replayState.getActiveSampler() != apiState.getActiveSampler())
4727 {
4728 cap(CaptureActiveTexture(replayState, true,
4729 GL_TEXTURE0 + static_cast<GLenum>(apiState.getActiveSampler())));
4730 replayState.getMutablePrivateStateForCapture()->setActiveSampler(
4731 apiState.getActiveSampler());
4732 }
4733
4734 // Set Renderbuffer binding.
4735 FramebufferCaptureFuncs framebufferFuncs(context->isGLES1());
4736
4737 const gl::RenderbufferManager &renderbuffers = apiState.getRenderbufferManagerForCapture();
4738 gl::RenderbufferID currentRenderbuffer = {0};
4739 for (const auto &renderbufIter : renderbuffers)
4740 {
4741 currentRenderbuffer = renderbufIter.second->id();
4742 }
4743
4744 if (currentRenderbuffer != apiState.getRenderbufferId())
4745 {
4746 cap(framebufferFuncs.bindRenderbuffer(replayState, true, GL_RENDERBUFFER,
4747 apiState.getRenderbufferId()));
4748 }
4749
4750 // Capture Framebuffers.
4751 const gl::FramebufferManager &framebuffers = apiState.getFramebufferManagerForCapture();
4752
4753 gl::FramebufferID currentDrawFramebuffer = {0};
4754 gl::FramebufferID currentReadFramebuffer = {0};
4755
4756 for (const auto &framebufferIter : framebuffers)
4757 {
4758 gl::FramebufferID id = {framebufferIter.first};
4759 const gl::Framebuffer *framebuffer = framebufferIter.second;
4760
4761 // The default Framebuffer exists (by default).
4762 if (framebuffer->isDefault())
4763 {
4764 continue;
4765 }
4766
4767 // Track this as a starting resource that may need to be restored
4768 TrackedResource &trackedFramebuffers =
4769 resourceTracker->getTrackedResource(context->id(), ResourceIDType::Framebuffer);
4770 ResourceSet &startingFramebuffers = trackedFramebuffers.getStartingResources();
4771 startingFramebuffers.insert(id.value);
4772
4773 // Create two lists of calls for initial setup
4774 ResourceCalls &framebufferRegenCalls = trackedFramebuffers.getResourceRegenCalls();
4775 CallVector framebufferGenCalls({setupCalls, &framebufferRegenCalls[id.value]});
4776
4777 // Gen the framebuffer
4778 for (std::vector<CallCapture> *calls : framebufferGenCalls)
4779 {
4780 Capture(calls, framebufferFuncs.genFramebuffers(replayState, true, 1, &id));
4781 MaybeCaptureUpdateResourceIDs(context, resourceTracker, calls);
4782 }
4783
4784 // Create two lists of calls for remaining setup calls. One for setup, and one for restore
4785 // during reset.
4786 ResourceCalls &framebufferRestoreCalls = trackedFramebuffers.getResourceRestoreCalls();
4787 CallVector framebufferSetupCalls({setupCalls, &framebufferRestoreCalls[id.value]});
4788
4789 for (std::vector<CallCapture> *calls : framebufferSetupCalls)
4790 {
4791 Capture(calls, framebufferFuncs.bindFramebuffer(replayState, true, GL_FRAMEBUFFER, id));
4792 }
4793 currentDrawFramebuffer = currentReadFramebuffer = id;
4794
4795 // Color Attachments.
4796 for (const gl::FramebufferAttachment &colorAttachment : framebuffer->getColorAttachments())
4797 {
4798 if (!colorAttachment.isAttached())
4799 {
4800 continue;
4801 }
4802
4803 for (std::vector<CallCapture> *calls : framebufferSetupCalls)
4804 {
4805 CaptureFramebufferAttachment(calls, replayState, framebufferFuncs, colorAttachment);
4806 }
4807 }
4808
4809 const gl::FramebufferAttachment *depthAttachment = framebuffer->getDepthAttachment();
4810 if (depthAttachment)
4811 {
4812 ASSERT(depthAttachment->getBinding() == GL_DEPTH_ATTACHMENT ||
4813 depthAttachment->getBinding() == GL_DEPTH_STENCIL_ATTACHMENT);
4814 for (std::vector<CallCapture> *calls : framebufferSetupCalls)
4815 {
4816 CaptureFramebufferAttachment(calls, replayState, framebufferFuncs,
4817 *depthAttachment);
4818 }
4819 }
4820
4821 const gl::FramebufferAttachment *stencilAttachment = framebuffer->getStencilAttachment();
4822 if (stencilAttachment)
4823 {
4824 ASSERT(stencilAttachment->getBinding() == GL_STENCIL_ATTACHMENT ||
4825 depthAttachment->getBinding() == GL_DEPTH_STENCIL_ATTACHMENT);
4826 for (std::vector<CallCapture> *calls : framebufferSetupCalls)
4827 {
4828 CaptureFramebufferAttachment(calls, replayState, framebufferFuncs,
4829 *stencilAttachment);
4830 }
4831 }
4832
4833 gl::FramebufferState defaultFramebufferState(
4834 context->getCaps(), framebuffer->getState().id(),
4835 framebuffer->getState().getFramebufferSerial());
4836 const gl::DrawBuffersVector<GLenum> &defaultDrawBufferStates =
4837 defaultFramebufferState.getDrawBufferStates();
4838 const gl::DrawBuffersVector<GLenum> &drawBufferStates = framebuffer->getDrawBufferStates();
4839
4840 if (drawBufferStates != defaultDrawBufferStates)
4841 {
4842 for (std::vector<CallCapture> *calls : framebufferSetupCalls)
4843 {
4844 Capture(calls, CaptureDrawBuffers(replayState, true,
4845 static_cast<GLsizei>(drawBufferStates.size()),
4846 drawBufferStates.data()));
4847 }
4848 }
4849 }
4850
4851 // Capture framebuffer bindings.
4852 if (apiState.getDrawFramebuffer())
4853 {
4854 ASSERT(apiState.getReadFramebuffer());
4855 gl::FramebufferID stateReadFramebuffer = apiState.getReadFramebuffer()->id();
4856 gl::FramebufferID stateDrawFramebuffer = apiState.getDrawFramebuffer()->id();
4857 if (stateDrawFramebuffer == stateReadFramebuffer)
4858 {
4859 if (currentDrawFramebuffer != stateDrawFramebuffer ||
4860 currentReadFramebuffer != stateReadFramebuffer)
4861 {
4862 cap(framebufferFuncs.bindFramebuffer(replayState, true, GL_FRAMEBUFFER,
4863 stateDrawFramebuffer));
4864 currentDrawFramebuffer = currentReadFramebuffer = stateDrawFramebuffer;
4865 }
4866 }
4867 else
4868 {
4869 if (currentDrawFramebuffer != stateDrawFramebuffer)
4870 {
4871 cap(framebufferFuncs.bindFramebuffer(replayState, true, GL_DRAW_FRAMEBUFFER,
4872 currentDrawFramebuffer));
4873 currentDrawFramebuffer = stateDrawFramebuffer;
4874 }
4875
4876 if (currentReadFramebuffer != stateReadFramebuffer)
4877 {
4878 cap(framebufferFuncs.bindFramebuffer(replayState, true, GL_READ_FRAMEBUFFER,
4879 replayState.getReadFramebuffer()->id()));
4880 currentReadFramebuffer = stateReadFramebuffer;
4881 }
4882 }
4883 }
4884
4885 // Capture Program Pipelines
4886 const gl::ProgramPipelineManager *programPipelineManager =
4887 apiState.getProgramPipelineManagerForCapture();
4888
4889 for (const auto &ppoIterator : *programPipelineManager)
4890 {
4891 gl::ProgramPipeline *pipeline = ppoIterator.second;
4892 gl::ProgramPipelineID id = {ppoIterator.first};
4893 cap(CaptureGenProgramPipelines(replayState, true, 1, &id));
4894 MaybeCaptureUpdateResourceIDs(context, resourceTracker, setupCalls);
4895
4896 // PPOs can contain graphics and compute programs, so loop through all shader types rather
4897 // than just the linked ones since getLinkedShaderStages() will return either only graphics
4898 // or compute stages.
4899 for (gl::ShaderType shaderType : gl::AllShaderTypes())
4900 {
4901 gl::Program *program = pipeline->getShaderProgram(shaderType);
4902 if (!program)
4903 {
4904 continue;
4905 }
4906 ASSERT(program->isLinked());
4907 GLbitfield gLbitfield = GetBitfieldFromShaderType(shaderType);
4908 cap(CaptureUseProgramStages(replayState, true, pipeline->id(), gLbitfield,
4909 program->id()));
4910
4911 // Set this program as active so it will be generated in Setup
4912 // Note: We aren't filtering ProgramPipelines, so this could be setting programs
4913 // active that aren't actually used.
4914 MarkResourceIDActive(ResourceIDType::ShaderProgram, program->id().value,
4915 shareGroupSetupCalls, resourceIDToSetupCalls);
4916 }
4917
4918 gl::Program *program = pipeline->getActiveShaderProgram();
4919 if (program)
4920 {
4921 cap(CaptureActiveShaderProgram(replayState, true, id, program->id()));
4922 }
4923 }
4924
4925 // For now we assume the installed program executable is the same as the current program.
4926 // TODO(jmadill): Handle installed program executable. http://anglebug.com/3662
4927 if (!context->isGLES1())
4928 {
4929 // If we have a program bound in the API, or if there is no program bound to the API at
4930 // time of capture and we bound a program for uniform updates during MEC, we must add
4931 // a set program call to replay the correct states.
4932 GLuint currentProgram = 0;
4933 if (apiState.getProgram())
4934 {
4935 cap(CaptureUseProgram(replayState, true, apiState.getProgram()->id()));
4936 CaptureUpdateCurrentProgram(setupCalls->back(), 0, setupCalls);
4937 (void)replayState.setProgram(context, apiState.getProgram());
4938
4939 // Set this program as active so it will be generated in Setup
4940 MarkResourceIDActive(ResourceIDType::ShaderProgram, apiState.getProgram()->id().value,
4941 shareGroupSetupCalls, resourceIDToSetupCalls);
4942
4943 currentProgram = apiState.getProgram()->id().value;
4944 }
4945 else if (replayState.getProgram())
4946 {
4947 cap(CaptureUseProgram(replayState, true, {0}));
4948 CaptureUpdateCurrentProgram(setupCalls->back(), 0, setupCalls);
4949 (void)replayState.setProgram(context, nullptr);
4950 }
4951
4952 // Track the calls necessary to reset active program back to initial state
4953 Capture(&resetCalls[angle::EntryPoint::GLUseProgram],
4954 CaptureUseProgram(replayState, true, {currentProgram}));
4955 CaptureUpdateCurrentProgram((&resetCalls[angle::EntryPoint::GLUseProgram])->back(), 0,
4956 &resetCalls[angle::EntryPoint::GLUseProgram]);
4957
4958 // Same for program pipelines as for programs, see comment above.
4959 if (apiState.getProgramPipeline())
4960 {
4961 cap(CaptureBindProgramPipeline(replayState, true, apiState.getProgramPipeline()->id()));
4962 }
4963 else if (replayState.getProgramPipeline())
4964 {
4965 cap(CaptureBindProgramPipeline(replayState, true, {0}));
4966 }
4967 }
4968
4969 // Create existing queries. Note that queries may be genned and not yet started. In that
4970 // case the queries will exist in the query map as nullptr entries.
4971 const gl::QueryMap &queryMap = context->getQueriesForCapture();
4972 for (gl::QueryMap::Iterator queryIter = queryMap.beginWithNull();
4973 queryIter != queryMap.endWithNull(); ++queryIter)
4974 {
4975 ASSERT(queryIter->first);
4976 gl::QueryID queryID = {queryIter->first};
4977
4978 cap(CaptureGenQueries(replayState, true, 1, &queryID));
4979 MaybeCaptureUpdateResourceIDs(context, resourceTracker, setupCalls);
4980
4981 gl::Query *query = queryIter->second;
4982 if (query)
4983 {
4984 gl::QueryType queryType = query->getType();
4985
4986 // Begin the query to generate the object
4987 cap(CaptureBeginQuery(replayState, true, queryType, queryID));
4988
4989 // End the query if it was not active
4990 if (!IsQueryActive(apiState, queryID))
4991 {
4992 cap(CaptureEndQuery(replayState, true, queryType));
4993 }
4994 }
4995 }
4996
4997 // Transform Feedback
4998 const gl::TransformFeedbackMap &xfbMap = context->getTransformFeedbacksForCapture();
4999 for (const auto &xfbIter : xfbMap)
5000 {
5001 gl::TransformFeedbackID xfbID = {xfbIter.first};
5002
5003 // Do not capture the default XFB object.
5004 if (xfbID.value == 0)
5005 {
5006 continue;
5007 }
5008
5009 cap(CaptureGenTransformFeedbacks(replayState, true, 1, &xfbID));
5010 MaybeCaptureUpdateResourceIDs(context, resourceTracker, setupCalls);
5011
5012 gl::TransformFeedback *xfb = xfbIter.second;
5013 if (!xfb)
5014 {
5015 // The object was never created
5016 continue;
5017 }
5018
5019 // Bind XFB to create the object
5020 cap(CaptureBindTransformFeedback(replayState, true, GL_TRANSFORM_FEEDBACK, xfbID));
5021
5022 // Bind the buffers associated with this XFB object
5023 for (size_t i = 0; i < xfb->getIndexedBufferCount(); ++i)
5024 {
5025 const gl::OffsetBindingPointer<gl::Buffer> &xfbBuffer = xfb->getIndexedBuffer(i);
5026
5027 // Note: Buffers bound with BindBufferBase can be used with BindBuffer
5028 cap(CaptureBindBufferRange(replayState, true, gl::BufferBinding::TransformFeedback, 0,
5029 xfbBuffer.id(), xfbBuffer.getOffset(), xfbBuffer.getSize()));
5030 }
5031
5032 if (xfb->isActive() || xfb->isPaused())
5033 {
5034 // We don't support active XFB in MEC yet
5035 UNIMPLEMENTED();
5036 }
5037 }
5038
5039 // Bind the current XFB buffer after populating XFB objects
5040 gl::TransformFeedback *currentXFB = apiState.getCurrentTransformFeedback();
5041 if (currentXFB)
5042 {
5043 cap(CaptureBindTransformFeedback(replayState, true, GL_TRANSFORM_FEEDBACK,
5044 currentXFB->id()));
5045 }
5046
5047 // Bind samplers
5048 const gl::SamplerBindingVector &samplerBindings = apiState.getSamplers();
5049 for (GLuint bindingIndex = 0; bindingIndex < static_cast<GLuint>(samplerBindings.size());
5050 ++bindingIndex)
5051 {
5052 gl::SamplerID samplerID = samplerBindings[bindingIndex].id();
5053 if (samplerID.value != 0)
5054 {
5055 cap(CaptureBindSampler(replayState, true, bindingIndex, samplerID));
5056 }
5057 }
5058
5059 // Capture Image Texture bindings
5060 const std::vector<gl::ImageUnit> &imageUnits = apiState.getImageUnits();
5061 for (GLuint bindingIndex = 0; bindingIndex < static_cast<GLuint>(imageUnits.size());
5062 ++bindingIndex)
5063 {
5064 const gl::ImageUnit &imageUnit = imageUnits[bindingIndex];
5065
5066 if (imageUnit.texture == 0)
5067 {
5068 continue;
5069 }
5070
5071 cap(CaptureBindImageTexture(replayState, true, bindingIndex, imageUnit.texture.id(),
5072 imageUnit.level, imageUnit.layered, imageUnit.layer,
5073 imageUnit.access, imageUnit.format));
5074 }
5075
5076 // Capture GL Context states.
5077 auto capCap = [cap, &replayState](GLenum capEnum, bool capValue) {
5078 if (capValue)
5079 {
5080 cap(CaptureEnable(replayState, true, capEnum));
5081 }
5082 else
5083 {
5084 cap(CaptureDisable(replayState, true, capEnum));
5085 }
5086 };
5087
5088 // Capture GLES1 context states.
5089 if (context->isGLES1())
5090 {
5091 const bool currentTextureState = apiState.getEnableFeature(GL_TEXTURE_2D);
5092 const bool defaultTextureState = replayState.getEnableFeature(GL_TEXTURE_2D);
5093 if (currentTextureState != defaultTextureState)
5094 {
5095 capCap(GL_TEXTURE_2D, currentTextureState);
5096 }
5097
5098 cap(CaptureMatrixMode(replayState, true, gl::MatrixType::Projection));
5099 for (angle::Mat4 projectionMatrix :
5100 apiState.gles1().getMatrixStack(gl::MatrixType::Projection))
5101 {
5102 cap(CapturePushMatrix(replayState, true));
5103 cap(CaptureLoadMatrixf(replayState, true, projectionMatrix.elements().data()));
5104 }
5105
5106 cap(CaptureMatrixMode(replayState, true, gl::MatrixType::Modelview));
5107 for (angle::Mat4 modelViewMatrix :
5108 apiState.gles1().getMatrixStack(gl::MatrixType::Modelview))
5109 {
5110 cap(CapturePushMatrix(replayState, true));
5111 cap(CaptureLoadMatrixf(replayState, true, modelViewMatrix.elements().data()));
5112 }
5113
5114 gl::MatrixType currentMatrixMode = apiState.gles1().getMatrixMode();
5115 if (currentMatrixMode != gl::MatrixType::Modelview)
5116 {
5117 cap(CaptureMatrixMode(replayState, true, currentMatrixMode));
5118 }
5119
5120 // Alpha Test state
5121 const bool currentAlphaTestState = apiState.getEnableFeature(GL_ALPHA_TEST);
5122 const bool defaultAlphaTestState = replayState.getEnableFeature(GL_ALPHA_TEST);
5123
5124 if (currentAlphaTestState != defaultAlphaTestState)
5125 {
5126 capCap(GL_ALPHA_TEST, currentAlphaTestState);
5127 }
5128
5129 const gl::AlphaTestParameters currentAlphaTestParameters =
5130 apiState.gles1().getAlphaTestParameters();
5131 const gl::AlphaTestParameters defaultAlphaTestParameters =
5132 replayState.gles1().getAlphaTestParameters();
5133
5134 if (currentAlphaTestParameters != defaultAlphaTestParameters)
5135 {
5136 cap(CaptureAlphaFunc(replayState, true, currentAlphaTestParameters.func,
5137 currentAlphaTestParameters.ref));
5138 }
5139 }
5140
5141 // Rasterizer state. Missing ES 3.x features.
5142 const gl::RasterizerState &defaultRasterState = replayState.getRasterizerState();
5143 const gl::RasterizerState ¤tRasterState = apiState.getRasterizerState();
5144 if (currentRasterState.cullFace != defaultRasterState.cullFace)
5145 {
5146 capCap(GL_CULL_FACE, currentRasterState.cullFace);
5147 }
5148
5149 if (currentRasterState.cullMode != defaultRasterState.cullMode)
5150 {
5151 cap(CaptureCullFace(replayState, true, currentRasterState.cullMode));
5152 }
5153
5154 if (currentRasterState.frontFace != defaultRasterState.frontFace)
5155 {
5156 cap(CaptureFrontFace(replayState, true, currentRasterState.frontFace));
5157 }
5158
5159 if (currentRasterState.polygonMode != defaultRasterState.polygonMode)
5160 {
5161 if (context->getExtensions().polygonModeNV)
5162 {
5163 cap(CapturePolygonModeNV(replayState, true, GL_FRONT_AND_BACK,
5164 currentRasterState.polygonMode));
5165 }
5166 else if (context->getExtensions().polygonModeANGLE)
5167 {
5168 cap(CapturePolygonModeANGLE(replayState, true, GL_FRONT_AND_BACK,
5169 currentRasterState.polygonMode));
5170 }
5171 else
5172 {
5173 UNREACHABLE();
5174 }
5175 }
5176
5177 if (currentRasterState.polygonOffsetPoint != defaultRasterState.polygonOffsetPoint)
5178 {
5179 capCap(GL_POLYGON_OFFSET_POINT_NV, currentRasterState.polygonOffsetPoint);
5180 }
5181
5182 if (currentRasterState.polygonOffsetLine != defaultRasterState.polygonOffsetLine)
5183 {
5184 capCap(GL_POLYGON_OFFSET_LINE_NV, currentRasterState.polygonOffsetLine);
5185 }
5186
5187 if (currentRasterState.polygonOffsetFill != defaultRasterState.polygonOffsetFill)
5188 {
5189 capCap(GL_POLYGON_OFFSET_FILL, currentRasterState.polygonOffsetFill);
5190 }
5191
5192 if (currentRasterState.polygonOffsetFactor != defaultRasterState.polygonOffsetFactor ||
5193 currentRasterState.polygonOffsetUnits != defaultRasterState.polygonOffsetUnits ||
5194 currentRasterState.polygonOffsetClamp != defaultRasterState.polygonOffsetClamp)
5195 {
5196 if (currentRasterState.polygonOffsetClamp == 0.0f)
5197 {
5198 cap(CapturePolygonOffset(replayState, true, currentRasterState.polygonOffsetFactor,
5199 currentRasterState.polygonOffsetUnits));
5200 }
5201 else
5202 {
5203 cap(CapturePolygonOffsetClampEXT(
5204 replayState, true, currentRasterState.polygonOffsetFactor,
5205 currentRasterState.polygonOffsetUnits, currentRasterState.polygonOffsetClamp));
5206 }
5207 }
5208
5209 if (currentRasterState.depthClamp != defaultRasterState.depthClamp)
5210 {
5211 capCap(GL_DEPTH_CLAMP_EXT, currentRasterState.depthClamp);
5212 }
5213
5214 // pointDrawMode/multiSample are only used in the D3D back-end right now.
5215
5216 if (currentRasterState.rasterizerDiscard != defaultRasterState.rasterizerDiscard)
5217 {
5218 capCap(GL_RASTERIZER_DISCARD, currentRasterState.rasterizerDiscard);
5219 }
5220
5221 if (currentRasterState.dither != defaultRasterState.dither)
5222 {
5223 capCap(GL_DITHER, currentRasterState.dither);
5224 }
5225
5226 // Depth/stencil state.
5227 const gl::DepthStencilState &defaultDSState = replayState.getDepthStencilState();
5228 const gl::DepthStencilState ¤tDSState = apiState.getDepthStencilState();
5229 if (defaultDSState.depthFunc != currentDSState.depthFunc)
5230 {
5231 cap(CaptureDepthFunc(replayState, true, currentDSState.depthFunc));
5232 }
5233
5234 if (defaultDSState.depthMask != currentDSState.depthMask)
5235 {
5236 cap(CaptureDepthMask(replayState, true, gl::ConvertToGLBoolean(currentDSState.depthMask)));
5237 }
5238
5239 if (defaultDSState.depthTest != currentDSState.depthTest)
5240 {
5241 capCap(GL_DEPTH_TEST, currentDSState.depthTest);
5242 }
5243
5244 if (defaultDSState.stencilTest != currentDSState.stencilTest)
5245 {
5246 capCap(GL_STENCIL_TEST, currentDSState.stencilTest);
5247 }
5248
5249 if (currentDSState.stencilFunc == currentDSState.stencilBackFunc &&
5250 currentDSState.stencilMask == currentDSState.stencilBackMask)
5251 {
5252 // Front and back are equal
5253 if (defaultDSState.stencilFunc != currentDSState.stencilFunc ||
5254 defaultDSState.stencilMask != currentDSState.stencilMask ||
5255 apiState.getStencilRef() != 0)
5256 {
5257 cap(CaptureStencilFunc(replayState, true, currentDSState.stencilFunc,
5258 apiState.getStencilRef(), currentDSState.stencilMask));
5259 }
5260 }
5261 else
5262 {
5263 // Front and back are separate
5264 if (defaultDSState.stencilFunc != currentDSState.stencilFunc ||
5265 defaultDSState.stencilMask != currentDSState.stencilMask ||
5266 apiState.getStencilRef() != 0)
5267 {
5268 cap(CaptureStencilFuncSeparate(replayState, true, GL_FRONT, currentDSState.stencilFunc,
5269 apiState.getStencilRef(), currentDSState.stencilMask));
5270 }
5271
5272 if (defaultDSState.stencilBackFunc != currentDSState.stencilBackFunc ||
5273 defaultDSState.stencilBackMask != currentDSState.stencilBackMask ||
5274 apiState.getStencilBackRef() != 0)
5275 {
5276 cap(CaptureStencilFuncSeparate(
5277 replayState, true, GL_BACK, currentDSState.stencilBackFunc,
5278 apiState.getStencilBackRef(), currentDSState.stencilBackMask));
5279 }
5280 }
5281
5282 if (currentDSState.stencilFail == currentDSState.stencilBackFail &&
5283 currentDSState.stencilPassDepthFail == currentDSState.stencilBackPassDepthFail &&
5284 currentDSState.stencilPassDepthPass == currentDSState.stencilBackPassDepthPass)
5285 {
5286 // Front and back are equal
5287 if (defaultDSState.stencilFail != currentDSState.stencilFail ||
5288 defaultDSState.stencilPassDepthFail != currentDSState.stencilPassDepthFail ||
5289 defaultDSState.stencilPassDepthPass != currentDSState.stencilPassDepthPass)
5290 {
5291 cap(CaptureStencilOp(replayState, true, currentDSState.stencilFail,
5292 currentDSState.stencilPassDepthFail,
5293 currentDSState.stencilPassDepthPass));
5294 }
5295 }
5296 else
5297 {
5298 // Front and back are separate
5299 if (defaultDSState.stencilFail != currentDSState.stencilFail ||
5300 defaultDSState.stencilPassDepthFail != currentDSState.stencilPassDepthFail ||
5301 defaultDSState.stencilPassDepthPass != currentDSState.stencilPassDepthPass)
5302 {
5303 cap(CaptureStencilOpSeparate(replayState, true, GL_FRONT, currentDSState.stencilFail,
5304 currentDSState.stencilPassDepthFail,
5305 currentDSState.stencilPassDepthPass));
5306 }
5307
5308 if (defaultDSState.stencilBackFail != currentDSState.stencilBackFail ||
5309 defaultDSState.stencilBackPassDepthFail != currentDSState.stencilBackPassDepthFail ||
5310 defaultDSState.stencilBackPassDepthPass != currentDSState.stencilBackPassDepthPass)
5311 {
5312 cap(CaptureStencilOpSeparate(replayState, true, GL_BACK, currentDSState.stencilBackFail,
5313 currentDSState.stencilBackPassDepthFail,
5314 currentDSState.stencilBackPassDepthPass));
5315 }
5316 }
5317
5318 if (currentDSState.stencilWritemask == currentDSState.stencilBackWritemask)
5319 {
5320 // Front and back are equal
5321 if (defaultDSState.stencilWritemask != currentDSState.stencilWritemask)
5322 {
5323 cap(CaptureStencilMask(replayState, true, currentDSState.stencilWritemask));
5324 }
5325 }
5326 else
5327 {
5328 // Front and back are separate
5329 if (defaultDSState.stencilWritemask != currentDSState.stencilWritemask)
5330 {
5331 cap(CaptureStencilMaskSeparate(replayState, true, GL_FRONT,
5332 currentDSState.stencilWritemask));
5333 }
5334
5335 if (defaultDSState.stencilBackWritemask != currentDSState.stencilBackWritemask)
5336 {
5337 cap(CaptureStencilMaskSeparate(replayState, true, GL_BACK,
5338 currentDSState.stencilBackWritemask));
5339 }
5340 }
5341
5342 // Blend state.
5343 const gl::BlendState &defaultBlendState = replayState.getBlendState();
5344 const gl::BlendState ¤tBlendState = apiState.getBlendState();
5345
5346 if (currentBlendState.blend != defaultBlendState.blend)
5347 {
5348 capCap(GL_BLEND, currentBlendState.blend);
5349 }
5350
5351 if (currentBlendState.sourceBlendRGB != defaultBlendState.sourceBlendRGB ||
5352 currentBlendState.destBlendRGB != defaultBlendState.destBlendRGB ||
5353 currentBlendState.sourceBlendAlpha != defaultBlendState.sourceBlendAlpha ||
5354 currentBlendState.destBlendAlpha != defaultBlendState.destBlendAlpha)
5355 {
5356 if (context->isGLES1())
5357 {
5358 // Even though their states are tracked independently, in GLES1 blendAlpha
5359 // and blendRGB cannot be set separately and are always equal
5360 cap(CaptureBlendFunc(replayState, true, currentBlendState.sourceBlendRGB,
5361 currentBlendState.destBlendRGB));
5362 Capture(&resetCalls[angle::EntryPoint::GLBlendFunc],
5363 CaptureBlendFunc(replayState, true, currentBlendState.sourceBlendRGB,
5364 currentBlendState.destBlendRGB));
5365 }
5366 else
5367 {
5368 // Always use BlendFuncSeparate for non-GLES1 as it covers all cases
5369 cap(CaptureBlendFuncSeparate(
5370 replayState, true, currentBlendState.sourceBlendRGB, currentBlendState.destBlendRGB,
5371 currentBlendState.sourceBlendAlpha, currentBlendState.destBlendAlpha));
5372 Capture(&resetCalls[angle::EntryPoint::GLBlendFuncSeparate],
5373 CaptureBlendFuncSeparate(replayState, true, currentBlendState.sourceBlendRGB,
5374 currentBlendState.destBlendRGB,
5375 currentBlendState.sourceBlendAlpha,
5376 currentBlendState.destBlendAlpha));
5377 }
5378 }
5379
5380 if (currentBlendState.blendEquationRGB != defaultBlendState.blendEquationRGB ||
5381 currentBlendState.blendEquationAlpha != defaultBlendState.blendEquationAlpha)
5382 {
5383 // Similarly to BlendFunc, using BlendEquation in some cases complicates Reset.
5384 cap(CaptureBlendEquationSeparate(replayState, true, currentBlendState.blendEquationRGB,
5385 currentBlendState.blendEquationAlpha));
5386 Capture(&resetCalls[angle::EntryPoint::GLBlendEquationSeparate],
5387 CaptureBlendEquationSeparate(replayState, true, currentBlendState.blendEquationRGB,
5388 currentBlendState.blendEquationAlpha));
5389 }
5390
5391 if (currentBlendState.colorMaskRed != defaultBlendState.colorMaskRed ||
5392 currentBlendState.colorMaskGreen != defaultBlendState.colorMaskGreen ||
5393 currentBlendState.colorMaskBlue != defaultBlendState.colorMaskBlue ||
5394 currentBlendState.colorMaskAlpha != defaultBlendState.colorMaskAlpha)
5395 {
5396 cap(CaptureColorMask(replayState, true,
5397 gl::ConvertToGLBoolean(currentBlendState.colorMaskRed),
5398 gl::ConvertToGLBoolean(currentBlendState.colorMaskGreen),
5399 gl::ConvertToGLBoolean(currentBlendState.colorMaskBlue),
5400 gl::ConvertToGLBoolean(currentBlendState.colorMaskAlpha)));
5401 Capture(&resetCalls[angle::EntryPoint::GLColorMask],
5402 CaptureColorMask(replayState, true,
5403 gl::ConvertToGLBoolean(currentBlendState.colorMaskRed),
5404 gl::ConvertToGLBoolean(currentBlendState.colorMaskGreen),
5405 gl::ConvertToGLBoolean(currentBlendState.colorMaskBlue),
5406 gl::ConvertToGLBoolean(currentBlendState.colorMaskAlpha)));
5407 }
5408
5409 const gl::ColorF ¤tBlendColor = apiState.getBlendColor();
5410 if (currentBlendColor != gl::ColorF())
5411 {
5412 cap(CaptureBlendColor(replayState, true, currentBlendColor.red, currentBlendColor.green,
5413 currentBlendColor.blue, currentBlendColor.alpha));
5414 Capture(&resetCalls[angle::EntryPoint::GLBlendColor],
5415 CaptureBlendColor(replayState, true, currentBlendColor.red, currentBlendColor.green,
5416 currentBlendColor.blue, currentBlendColor.alpha));
5417 }
5418
5419 // Pixel storage states.
5420 gl::PixelPackState ¤tPackState = replayState.getPackState();
5421 if (currentPackState.alignment != apiState.getPackAlignment())
5422 {
5423 cap(CapturePixelStorei(replayState, true, GL_PACK_ALIGNMENT, apiState.getPackAlignment()));
5424 currentPackState.alignment = apiState.getPackAlignment();
5425 }
5426
5427 if (currentPackState.rowLength != apiState.getPackRowLength())
5428 {
5429 cap(CapturePixelStorei(replayState, true, GL_PACK_ROW_LENGTH, apiState.getPackRowLength()));
5430 currentPackState.rowLength = apiState.getPackRowLength();
5431 }
5432
5433 if (currentPackState.skipRows != apiState.getPackSkipRows())
5434 {
5435 cap(CapturePixelStorei(replayState, true, GL_PACK_SKIP_ROWS, apiState.getPackSkipRows()));
5436 currentPackState.skipRows = apiState.getPackSkipRows();
5437 }
5438
5439 if (currentPackState.skipPixels != apiState.getPackSkipPixels())
5440 {
5441 cap(CapturePixelStorei(replayState, true, GL_PACK_SKIP_PIXELS,
5442 apiState.getPackSkipPixels()));
5443 currentPackState.skipPixels = apiState.getPackSkipPixels();
5444 }
5445
5446 // We set unpack alignment above, no need to change it here
5447 ASSERT(currentUnpackState.alignment == 1);
5448 if (currentUnpackState.rowLength != apiState.getUnpackRowLength())
5449 {
5450 cap(CapturePixelStorei(replayState, true, GL_UNPACK_ROW_LENGTH,
5451 apiState.getUnpackRowLength()));
5452 currentUnpackState.rowLength = apiState.getUnpackRowLength();
5453 }
5454
5455 if (currentUnpackState.skipRows != apiState.getUnpackSkipRows())
5456 {
5457 cap(CapturePixelStorei(replayState, true, GL_UNPACK_SKIP_ROWS,
5458 apiState.getUnpackSkipRows()));
5459 currentUnpackState.skipRows = apiState.getUnpackSkipRows();
5460 }
5461
5462 if (currentUnpackState.skipPixels != apiState.getUnpackSkipPixels())
5463 {
5464 cap(CapturePixelStorei(replayState, true, GL_UNPACK_SKIP_PIXELS,
5465 apiState.getUnpackSkipPixels()));
5466 currentUnpackState.skipPixels = apiState.getUnpackSkipPixels();
5467 }
5468
5469 if (currentUnpackState.imageHeight != apiState.getUnpackImageHeight())
5470 {
5471 cap(CapturePixelStorei(replayState, true, GL_UNPACK_IMAGE_HEIGHT,
5472 apiState.getUnpackImageHeight()));
5473 currentUnpackState.imageHeight = apiState.getUnpackImageHeight();
5474 }
5475
5476 if (currentUnpackState.skipImages != apiState.getUnpackSkipImages())
5477 {
5478 cap(CapturePixelStorei(replayState, true, GL_UNPACK_SKIP_IMAGES,
5479 apiState.getUnpackSkipImages()));
5480 currentUnpackState.skipImages = apiState.getUnpackSkipImages();
5481 }
5482
5483 // Clear state. Missing ES 3.x features.
5484 // TODO(http://anglebug.com/3662): Complete state capture.
5485 const gl::ColorF ¤tClearColor = apiState.getColorClearValue();
5486 if (currentClearColor != gl::ColorF())
5487 {
5488 cap(CaptureClearColor(replayState, true, currentClearColor.red, currentClearColor.green,
5489 currentClearColor.blue, currentClearColor.alpha));
5490 }
5491
5492 if (apiState.getDepthClearValue() != 1.0f)
5493 {
5494 cap(CaptureClearDepthf(replayState, true, apiState.getDepthClearValue()));
5495 }
5496
5497 if (apiState.getStencilClearValue() != 0)
5498 {
5499 cap(CaptureClearStencil(replayState, true, apiState.getStencilClearValue()));
5500 }
5501
5502 // Viewport / scissor / clipping planes.
5503 const gl::Rectangle ¤tViewport = apiState.getViewport();
5504 if (currentViewport != gl::Rectangle())
5505 {
5506 cap(CaptureViewport(replayState, true, currentViewport.x, currentViewport.y,
5507 currentViewport.width, currentViewport.height));
5508 }
5509
5510 if (apiState.getNearPlane() != 0.0f || apiState.getFarPlane() != 1.0f)
5511 {
5512 cap(CaptureDepthRangef(replayState, true, apiState.getNearPlane(), apiState.getFarPlane()));
5513 }
5514
5515 if (apiState.getClipOrigin() != gl::ClipOrigin::LowerLeft ||
5516 apiState.getClipDepthMode() != gl::ClipDepthMode::NegativeOneToOne)
5517 {
5518 cap(CaptureClipControlEXT(replayState, true, apiState.getClipOrigin(),
5519 apiState.getClipDepthMode()));
5520 }
5521
5522 if (apiState.isScissorTestEnabled())
5523 {
5524 capCap(GL_SCISSOR_TEST, apiState.isScissorTestEnabled());
5525 }
5526
5527 const gl::Rectangle ¤tScissor = apiState.getScissor();
5528 if (currentScissor != gl::Rectangle())
5529 {
5530 cap(CaptureScissor(replayState, true, currentScissor.x, currentScissor.y,
5531 currentScissor.width, currentScissor.height));
5532 }
5533
5534 // Allow the replayState object to be destroyed conveniently.
5535 replayState.setBufferBinding(context, gl::BufferBinding::Array, nullptr);
5536
5537 // Clean up the replay state.
5538 replayState.reset(context);
5539
5540 GLint contextUnpackAlignment = context->getState().getUnpackState().alignment;
5541 if (currentUnpackState.alignment != contextUnpackAlignment)
5542 {
5543 cap(CapturePixelStorei(replayState, true, GL_UNPACK_ALIGNMENT, contextUnpackAlignment));
5544 replayState.getMutablePrivateStateForCapture()->setUnpackAlignment(contextUnpackAlignment);
5545 }
5546
5547 if (validationEnabled)
5548 {
5549 CaptureValidateSerializedState(context, setupCalls);
5550 }
5551 }
5552
SkipCall(EntryPoint entryPoint)5553 bool SkipCall(EntryPoint entryPoint)
5554 {
5555 switch (entryPoint)
5556 {
5557 case EntryPoint::GLDebugMessageCallback:
5558 case EntryPoint::GLDebugMessageCallbackKHR:
5559 case EntryPoint::GLDebugMessageControl:
5560 case EntryPoint::GLDebugMessageControlKHR:
5561 case EntryPoint::GLDebugMessageInsert:
5562 case EntryPoint::GLDebugMessageInsertKHR:
5563 case EntryPoint::GLGetDebugMessageLog:
5564 case EntryPoint::GLGetDebugMessageLogKHR:
5565 case EntryPoint::GLGetObjectLabel:
5566 case EntryPoint::GLGetObjectLabelEXT:
5567 case EntryPoint::GLGetObjectLabelKHR:
5568 case EntryPoint::GLGetObjectPtrLabelKHR:
5569 case EntryPoint::GLGetPointervKHR:
5570 case EntryPoint::GLInsertEventMarkerEXT:
5571 case EntryPoint::GLLabelObjectEXT:
5572 case EntryPoint::GLObjectLabel:
5573 case EntryPoint::GLObjectLabelKHR:
5574 case EntryPoint::GLObjectPtrLabelKHR:
5575 case EntryPoint::GLPopDebugGroupKHR:
5576 case EntryPoint::GLPopGroupMarkerEXT:
5577 case EntryPoint::GLPushDebugGroupKHR:
5578 case EntryPoint::GLPushGroupMarkerEXT:
5579 // Purposefully skip entry points from:
5580 // - KHR_debug
5581 // - EXT_debug_label
5582 // - EXT_debug_marker
5583 // There is no need to capture these for replaying a trace in our harness
5584 return true;
5585
5586 case EntryPoint::GLGetActiveUniform:
5587 case EntryPoint::GLGetActiveUniformsiv:
5588 // Skip these calls because:
5589 // - We don't use the return values.
5590 // - Active uniform counts can vary between platforms due to cross stage optimizations
5591 // and asking about uniforms above GL_ACTIVE_UNIFORMS triggers errors.
5592 return true;
5593
5594 case EntryPoint::GLGetActiveAttrib:
5595 // Skip these calls because:
5596 // - We don't use the return values.
5597 // - Same as uniforms, the value can vary, asking above GL_ACTIVE_ATTRIBUTES is an error
5598 return true;
5599
5600 case EntryPoint::GLGetActiveUniformBlockiv:
5601 case EntryPoint::GLGetActiveUniformName:
5602 case EntryPoint::GLGetActiveUniformBlockName:
5603 // Skip these calls because:
5604 // - We don't use the return values.
5605 // - It reduces the number of references to the uniform block index map.
5606 return true;
5607
5608 case EntryPoint::EGLChooseConfig:
5609 case EntryPoint::EGLGetProcAddress:
5610 case EntryPoint::EGLGetConfigAttrib:
5611 case EntryPoint::EGLGetConfigs:
5612 case EntryPoint::EGLGetSyncAttrib:
5613 case EntryPoint::EGLGetSyncAttribKHR:
5614 case EntryPoint::EGLQuerySurface:
5615 // Skip these calls because:
5616 // - Some EGL types and pointer parameters aren't yet implemented in EGL capture.
5617 return true;
5618
5619 case EntryPoint::EGLPrepareSwapBuffersANGLE:
5620 // Skip this call because:
5621 // - eglPrepareSwapBuffersANGLE is automatically called by eglSwapBuffers
5622 return true;
5623
5624 case EntryPoint::EGLSwapBuffers:
5625 // Skip these calls because:
5626 // - Swap is handled specially by the trace harness.
5627 return true;
5628
5629 default:
5630 break;
5631 }
5632
5633 return false;
5634 }
5635
5636 template <typename ParamValueType>
5637 struct ParamValueTrait
5638 {
5639 static_assert(sizeof(ParamValueType) == 0, "invalid ParamValueType");
5640 };
5641
5642 template <>
5643 struct ParamValueTrait<gl::FramebufferID>
5644 {
5645 static constexpr const char *name = "framebufferPacked";
5646 static const ParamType typeID = ParamType::TFramebufferID;
5647 };
5648
GetBaseName(const std::string & nameWithPath)5649 std::string GetBaseName(const std::string &nameWithPath)
5650 {
5651 std::vector<std::string> result = angle::SplitString(
5652 nameWithPath, "/\\", WhitespaceHandling::TRIM_WHITESPACE, SplitResult::SPLIT_WANT_NONEMPTY);
5653 ASSERT(!result.empty());
5654 return result.back();
5655 }
5656
5657 template <>
5658 struct ParamValueTrait<gl::BufferID>
5659 {
5660 static constexpr const char *name = "bufferPacked";
5661 static const ParamType typeID = ParamType::TBufferID;
5662 };
5663
5664 template <>
5665 struct ParamValueTrait<gl::RenderbufferID>
5666 {
5667 static constexpr const char *name = "renderbufferPacked";
5668 static const ParamType typeID = ParamType::TRenderbufferID;
5669 };
5670
5671 template <>
5672 struct ParamValueTrait<gl::TextureID>
5673 {
5674 static constexpr const char *name = "texturePacked";
5675 static const ParamType typeID = ParamType::TTextureID;
5676 };
5677 } // namespace
5678
5679 FrameCapture::FrameCapture() = default;
5680 FrameCapture::~FrameCapture() = default;
5681
reset()5682 void FrameCapture::reset()
5683 {
5684 mSetupCalls.clear();
5685 }
5686
FrameCaptureShared()5687 FrameCaptureShared::FrameCaptureShared()
5688 : mLastContextId{0},
5689 mEnabled(true),
5690 mSerializeStateEnabled(false),
5691 mCompression(true),
5692 mClientVertexArrayMap{},
5693 mFrameIndex(1),
5694 mCaptureStartFrame(1),
5695 mCaptureEndFrame(0),
5696 mClientArraySizes{},
5697 mReadBufferSize(0),
5698 mResourceIDBufferSize(0),
5699 mHasResourceType{},
5700 mResourceIDToSetupCalls{},
5701 mMaxAccessedResourceIDs{},
5702 mCaptureTrigger(0),
5703 mCaptureActive(false),
5704 mWindowSurfaceContextID({0})
5705 {
5706 reset();
5707
5708 std::string enabledFromEnv =
5709 GetEnvironmentVarOrUnCachedAndroidProperty(kEnabledVarName, kAndroidEnabled);
5710 if (enabledFromEnv == "0")
5711 {
5712 mEnabled = false;
5713 }
5714
5715 std::string pathFromEnv =
5716 GetEnvironmentVarOrUnCachedAndroidProperty(kOutDirectoryVarName, kAndroidOutDir);
5717 if (pathFromEnv.empty())
5718 {
5719 mOutDirectory = GetDefaultOutDirectory();
5720 }
5721 else
5722 {
5723 mOutDirectory = pathFromEnv;
5724 }
5725
5726 // Ensure the capture path ends with a slash.
5727 if (mOutDirectory.back() != '\\' && mOutDirectory.back() != '/')
5728 {
5729 mOutDirectory += '/';
5730 }
5731
5732 std::string startFromEnv =
5733 GetEnvironmentVarOrUnCachedAndroidProperty(kFrameStartVarName, kAndroidFrameStart);
5734 if (!startFromEnv.empty())
5735 {
5736 mCaptureStartFrame = atoi(startFromEnv.c_str());
5737 }
5738 if (mCaptureStartFrame < 1)
5739 {
5740 WARN() << "Cannot use a capture start frame less than 1.";
5741 mCaptureStartFrame = 1;
5742 }
5743
5744 std::string endFromEnv =
5745 GetEnvironmentVarOrUnCachedAndroidProperty(kFrameEndVarName, kAndroidFrameEnd);
5746 if (!endFromEnv.empty())
5747 {
5748 mCaptureEndFrame = atoi(endFromEnv.c_str());
5749 }
5750
5751 std::string captureTriggerFromEnv =
5752 GetEnvironmentVarOrUnCachedAndroidProperty(kTriggerVarName, kAndroidTrigger);
5753 if (!captureTriggerFromEnv.empty())
5754 {
5755 mCaptureTrigger = atoi(captureTriggerFromEnv.c_str());
5756
5757 // If the trigger has been populated, ignore the other frame range variables by setting them
5758 // to unreasonable values. This isn't perfect, but it is effective.
5759 mCaptureStartFrame = mCaptureEndFrame = std::numeric_limits<uint32_t>::max();
5760 INFO() << "Capture trigger detected, disabling capture start/end frame.";
5761 }
5762
5763 std::string labelFromEnv =
5764 GetEnvironmentVarOrUnCachedAndroidProperty(kCaptureLabelVarName, kAndroidCaptureLabel);
5765 if (!labelFromEnv.empty())
5766 {
5767 // Optional label to provide unique file names and namespaces
5768 mCaptureLabel = labelFromEnv;
5769 }
5770
5771 std::string compressionFromEnv =
5772 GetEnvironmentVarOrUnCachedAndroidProperty(kCompressionVarName, kAndroidCompression);
5773 if (compressionFromEnv == "0")
5774 {
5775 mCompression = false;
5776 }
5777 std::string serializeStateFromEnv = angle::GetEnvironmentVar(kSerializeStateVarName);
5778 if (serializeStateFromEnv == "1")
5779 {
5780 mSerializeStateEnabled = true;
5781 }
5782
5783 std::string validateSerialiedStateFromEnv =
5784 GetEnvironmentVarOrUnCachedAndroidProperty(kValidationVarName, kAndroidValidation);
5785 if (validateSerialiedStateFromEnv == "1")
5786 {
5787 mValidateSerializedState = true;
5788 }
5789
5790 mValidationExpression =
5791 GetEnvironmentVarOrUnCachedAndroidProperty(kValidationExprVarName, kAndroidValidationExpr);
5792
5793 if (!mValidationExpression.empty())
5794 {
5795 INFO() << "Validation expression is " << kValidationExprVarName;
5796 }
5797
5798 std::string trimEnabledFromEnv =
5799 GetEnvironmentVarOrUnCachedAndroidProperty(kTrimEnabledVarName, kAndroidTrimEnabled);
5800 if (trimEnabledFromEnv == "0")
5801 {
5802 mTrimEnabled = false;
5803 }
5804
5805 // TODO: Remove. http://anglebug.com/7753
5806 std::string sourceExtFromEnv =
5807 GetEnvironmentVarOrUnCachedAndroidProperty(kSourceExtVarName, kAndroidSourceExt);
5808 if (!sourceExtFromEnv.empty())
5809 {
5810 if (sourceExtFromEnv == "c" || sourceExtFromEnv == "cpp")
5811 {
5812 mReplayWriter.setSourceFileExtension(sourceExtFromEnv.c_str());
5813 }
5814 else
5815 {
5816 WARN() << "Invalid capture source extension: " << sourceExtFromEnv;
5817 }
5818 }
5819
5820 std::string sourceSizeFromEnv =
5821 GetEnvironmentVarOrUnCachedAndroidProperty(kSourceSizeVarName, kAndroidSourceSize);
5822 if (!sourceSizeFromEnv.empty())
5823 {
5824 int sourceSize = atoi(sourceSizeFromEnv.c_str());
5825 if (sourceSize < 0)
5826 {
5827 WARN() << "Invalid capture source size: " << sourceSize;
5828 }
5829 else
5830 {
5831 mReplayWriter.setSourceFileSizeThreshold(sourceSize);
5832 }
5833 }
5834
5835 std::string forceShadowFromEnv =
5836 GetEnvironmentVarOrUnCachedAndroidProperty(kForceShadowVarName, kAndroidForceShadow);
5837 if (forceShadowFromEnv == "1")
5838 {
5839 INFO() << "Force enabling shadow memory for coherent buffer tracking.";
5840 mCoherentBufferTracker.enableShadowMemory();
5841 }
5842
5843 if (mFrameIndex == mCaptureStartFrame)
5844 {
5845 // Capture is starting from the first frame, so set the capture active to ensure all GLES
5846 // commands issued are handled correctly by maybeCapturePreCallUpdates() and
5847 // maybeCapturePostCallUpdates().
5848 setCaptureActive();
5849 }
5850
5851 if (mCaptureEndFrame < mCaptureStartFrame)
5852 {
5853 mEnabled = false;
5854 }
5855
5856 mReplayWriter.setCaptureLabel(mCaptureLabel);
5857 }
5858
5859 FrameCaptureShared::~FrameCaptureShared() = default;
5860
PageRange(size_t start,size_t end)5861 PageRange::PageRange(size_t start, size_t end) : start(start), end(end) {}
5862 PageRange::~PageRange() = default;
5863
AddressRange()5864 AddressRange::AddressRange() {}
AddressRange(uintptr_t start,size_t size)5865 AddressRange::AddressRange(uintptr_t start, size_t size) : start(start), size(size) {}
5866 AddressRange::~AddressRange() = default;
5867
end()5868 uintptr_t AddressRange::end()
5869 {
5870 return start + size;
5871 }
5872
CoherentBuffer(uintptr_t start,size_t size,size_t pageSize,bool isShadowMemoryEnabled)5873 CoherentBuffer::CoherentBuffer(uintptr_t start,
5874 size_t size,
5875 size_t pageSize,
5876 bool isShadowMemoryEnabled)
5877 : mPageSize(pageSize),
5878 mShadowMemoryEnabled(isShadowMemoryEnabled),
5879 mBufferStart(start),
5880 mShadowMemory(nullptr),
5881 mShadowDirty(false)
5882 {
5883 if (mShadowMemoryEnabled)
5884 {
5885 // Shadow memory needs to have at least the size of one page, to not protect outside.
5886 size_t numShadowPages = (size / pageSize) + 1;
5887 mShadowMemory = AlignedAlloc(numShadowPages * pageSize, pageSize);
5888 ASSERT(mShadowMemory != nullptr);
5889 start = reinterpret_cast<uintptr_t>(mShadowMemory);
5890 }
5891
5892 mRange.start = start;
5893 mRange.size = size;
5894 mProtectionRange.start = rx::roundDownPow2(start, pageSize);
5895
5896 uintptr_t protectionEnd = rx::roundUpPow2(start + size, pageSize);
5897
5898 mProtectionRange.size = protectionEnd - mProtectionRange.start;
5899 mPageCount = mProtectionRange.size / pageSize;
5900
5901 mProtectionStartPage = mProtectionRange.start / mPageSize;
5902 mProtectionEndPage = mProtectionStartPage + mPageCount;
5903
5904 mDirtyPages = std::vector<bool>(mPageCount);
5905 mDirtyPages.assign(mPageCount, true);
5906 }
5907
getDirtyPageRanges()5908 std::vector<PageRange> CoherentBuffer::getDirtyPageRanges()
5909 {
5910 std::vector<PageRange> dirtyPageRanges;
5911
5912 bool inDirty = false;
5913 for (size_t i = 0; i < mPageCount; i++)
5914 {
5915 if (!inDirty && mDirtyPages[i])
5916 {
5917 // Found start of a dirty range
5918 inDirty = true;
5919 // Set end page as last page initially
5920 dirtyPageRanges.push_back(PageRange(i, mPageCount));
5921 }
5922 else if (inDirty && !mDirtyPages[i])
5923 {
5924 // Found end of a dirty range
5925 inDirty = false;
5926 dirtyPageRanges.back().end = i;
5927 }
5928 }
5929
5930 return dirtyPageRanges;
5931 }
5932
getRange()5933 AddressRange CoherentBuffer::getRange()
5934 {
5935 return mRange;
5936 }
5937
getDirtyAddressRange(const PageRange & dirtyPageRange)5938 AddressRange CoherentBuffer::getDirtyAddressRange(const PageRange &dirtyPageRange)
5939 {
5940 AddressRange range;
5941
5942 if (dirtyPageRange.start == 0)
5943 {
5944 // First page, use non page aligned buffer start.
5945 range.start = mRange.start;
5946 }
5947 else
5948 {
5949 range.start = mProtectionRange.start + dirtyPageRange.start * mPageSize;
5950 }
5951
5952 if (dirtyPageRange.end == mPageCount)
5953 {
5954 // Last page, use non page aligned buffer end.
5955 range.size = mRange.end() - range.start;
5956 }
5957 else
5958 {
5959 range.size = (dirtyPageRange.end - dirtyPageRange.start) * mPageSize;
5960 // This occurs when a buffer occupies 2 pages, but is smaller than a page.
5961 if (mRange.end() < range.end())
5962 {
5963 range.size = mRange.end() - range.start;
5964 }
5965 }
5966
5967 // Dirty range must be in buffer
5968 ASSERT(range.start >= mRange.start && mRange.end() >= range.end());
5969
5970 return range;
5971 }
5972
~CoherentBuffer()5973 CoherentBuffer::~CoherentBuffer()
5974 {
5975 if (mShadowMemory != nullptr)
5976 {
5977 AlignedFree(mShadowMemory);
5978 }
5979 }
5980
isDirty()5981 bool CoherentBuffer::isDirty()
5982 {
5983 return std::find(mDirtyPages.begin(), mDirtyPages.end(), true) != mDirtyPages.end();
5984 }
5985
contains(size_t page,size_t * relativePage)5986 bool CoherentBuffer::contains(size_t page, size_t *relativePage)
5987 {
5988 bool isInProtectionRange = page >= mProtectionStartPage && page < mProtectionEndPage;
5989 if (!isInProtectionRange)
5990 {
5991 return false;
5992 }
5993
5994 *relativePage = page - mProtectionStartPage;
5995
5996 ASSERT(page >= mProtectionStartPage);
5997
5998 return true;
5999 }
6000
protectPageRange(const PageRange & pageRange)6001 void CoherentBuffer::protectPageRange(const PageRange &pageRange)
6002 {
6003 for (size_t i = pageRange.start; i < pageRange.end; i++)
6004 {
6005 setDirty(i, false);
6006 }
6007 }
6008
protectAll()6009 void CoherentBuffer::protectAll()
6010 {
6011 for (size_t i = 0; i < mPageCount; i++)
6012 {
6013 setDirty(i, false);
6014 }
6015 }
6016
updateBufferMemory()6017 void CoherentBuffer::updateBufferMemory()
6018 {
6019 memcpy(reinterpret_cast<void *>(mBufferStart), reinterpret_cast<void *>(mRange.start),
6020 mRange.size);
6021 }
6022
updateShadowMemory()6023 void CoherentBuffer::updateShadowMemory()
6024 {
6025 memcpy(reinterpret_cast<void *>(mRange.start), reinterpret_cast<void *>(mBufferStart),
6026 mRange.size);
6027 mShadowDirty = false;
6028 }
6029
setDirty(size_t relativePage,bool dirty)6030 void CoherentBuffer::setDirty(size_t relativePage, bool dirty)
6031 {
6032 if (mDirtyPages[relativePage] == dirty)
6033 {
6034 // The page is already set.
6035 // This can happen when tracked buffers overlap in a page.
6036 return;
6037 }
6038
6039 uintptr_t pageStart = mProtectionRange.start + relativePage * mPageSize;
6040
6041 // Last page end must be the same as protection end
6042 if (relativePage + 1 == mPageCount)
6043 {
6044 ASSERT(mProtectionRange.end() == pageStart + mPageSize);
6045 }
6046
6047 bool ret;
6048 if (dirty)
6049 {
6050 ret = UnprotectMemory(pageStart, mPageSize);
6051 }
6052 else
6053 {
6054 ret = ProtectMemory(pageStart, mPageSize);
6055 }
6056
6057 if (!ret)
6058 {
6059 ERR() << "Could not set protection for buffer page " << relativePage << " at "
6060 << reinterpret_cast<void *>(pageStart) << " with size " << mPageSize;
6061 }
6062 mDirtyPages[relativePage] = dirty;
6063 }
6064
removeProtection(PageSharingType sharingType)6065 void CoherentBuffer::removeProtection(PageSharingType sharingType)
6066 {
6067 uintptr_t start = mProtectionRange.start;
6068 size_t size = mProtectionRange.size;
6069
6070 switch (sharingType)
6071 {
6072 case PageSharingType::FirstShared:
6073 case PageSharingType::FirstAndLastShared:
6074 start += mPageSize;
6075 break;
6076 default:
6077 break;
6078 }
6079
6080 switch (sharingType)
6081 {
6082 case PageSharingType::FirstShared:
6083 case PageSharingType::LastShared:
6084 size -= mPageSize;
6085 break;
6086 case PageSharingType::FirstAndLastShared:
6087 size -= (2 * mPageSize);
6088 break;
6089 default:
6090 break;
6091 }
6092
6093 if (size == 0)
6094 {
6095 return;
6096 }
6097
6098 if (!UnprotectMemory(start, size))
6099 {
6100 ERR() << "Could not remove protection for buffer at " << start << " with size " << size;
6101 }
6102 }
6103
canProtectDirectly(gl::Context * context)6104 bool CoherentBufferTracker::canProtectDirectly(gl::Context *context)
6105 {
6106 gl::BufferID bufferId = context->createBuffer();
6107
6108 gl::BufferBinding targetPacked = gl::BufferBinding::Array;
6109 context->bindBuffer(targetPacked, bufferId);
6110
6111 // Allocate 2 pages so we will always have a full aligned page to protect
6112 GLsizei size = static_cast<GLsizei>(mPageSize * 2);
6113
6114 context->bufferStorage(targetPacked, size, nullptr,
6115 GL_DYNAMIC_STORAGE_BIT_EXT | GL_MAP_WRITE_BIT |
6116 GL_MAP_PERSISTENT_BIT_EXT | GL_MAP_COHERENT_BIT_EXT);
6117
6118 gl::Buffer *buffer = context->getBuffer(bufferId);
6119
6120 angle::Result result = buffer->mapRange(
6121 context, 0, size, GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT_EXT | GL_MAP_COHERENT_BIT_EXT);
6122 if (result != angle::Result::Continue)
6123 {
6124 ERR() << "Failed to mapRange of buffer.";
6125 }
6126
6127 void *map = buffer->getMapPointer();
6128 if (map == nullptr)
6129 {
6130 ERR() << "Failed to getMapPointer of buffer.";
6131 }
6132
6133 // Test mprotect
6134 auto start = reinterpret_cast<uintptr_t>(map);
6135
6136 // Only protect a whole page inside the allocated memory
6137 uintptr_t protectionStart = rx::roundUpPow2(start, mPageSize);
6138 uintptr_t protectionEnd = protectionStart + mPageSize;
6139
6140 ASSERT(protectionStart < protectionEnd);
6141
6142 angle::PageFaultCallback callback = [](uintptr_t address) {
6143 return angle::PageFaultHandlerRangeType::InRange;
6144 };
6145
6146 std::unique_ptr<angle::PageFaultHandler> handler(CreatePageFaultHandler(callback));
6147
6148 if (!handler->enable())
6149 {
6150 GLboolean unmapResult;
6151 if (buffer->unmap(context, &unmapResult) != angle::Result::Continue)
6152 {
6153 ERR() << "Could not unmap buffer.";
6154 }
6155 context->bindBuffer(targetPacked, {0});
6156
6157 // Page fault handler could not be enabled, memory can't be protected directly.
6158 return false;
6159 }
6160
6161 size_t protectionSize = protectionEnd - protectionStart;
6162
6163 ASSERT(protectionSize == mPageSize);
6164
6165 bool canProtect = angle::ProtectMemory(protectionStart, protectionSize);
6166 if (canProtect)
6167 {
6168 angle::UnprotectMemory(protectionStart, protectionSize);
6169 }
6170
6171 // Clean up
6172 handler->disable();
6173
6174 GLboolean unmapResult;
6175 if (buffer->unmap(context, &unmapResult) != angle::Result::Continue)
6176 {
6177 ERR() << "Could not unmap buffer.";
6178 }
6179 context->bindBuffer(targetPacked, {0});
6180 context->deleteBuffer(buffer->id());
6181
6182 return canProtect;
6183 }
6184
CoherentBufferTracker()6185 CoherentBufferTracker::CoherentBufferTracker() : mEnabled(false), mShadowMemoryEnabled(false)
6186 {
6187 mPageSize = GetPageSize();
6188 }
6189
~CoherentBufferTracker()6190 CoherentBufferTracker::~CoherentBufferTracker()
6191 {
6192 disable();
6193 }
6194
handleWrite(uintptr_t address)6195 PageFaultHandlerRangeType CoherentBufferTracker::handleWrite(uintptr_t address)
6196 {
6197 std::lock_guard<std::mutex> lock(mMutex);
6198 auto pagesInBuffers = getBufferPagesForAddress(address);
6199
6200 if (pagesInBuffers.empty())
6201 {
6202 ERR() << "Didn't find a tracked buffer containing " << reinterpret_cast<void *>(address);
6203 }
6204
6205 for (const auto &page : pagesInBuffers)
6206 {
6207 std::shared_ptr<CoherentBuffer> buffer = page.first;
6208 size_t relativePage = page.second;
6209 buffer->setDirty(relativePage, true);
6210 }
6211
6212 return pagesInBuffers.empty() ? PageFaultHandlerRangeType::OutOfRange
6213 : PageFaultHandlerRangeType::InRange;
6214 }
6215
getBufferPagesForAddress(uintptr_t address)6216 HashMap<std::shared_ptr<CoherentBuffer>, size_t> CoherentBufferTracker::getBufferPagesForAddress(
6217 uintptr_t address)
6218 {
6219 HashMap<std::shared_ptr<CoherentBuffer>, size_t> foundPages;
6220
6221 #if defined(ANGLE_PLATFORM_ANDROID)
6222 size_t page;
6223 if (mShadowMemoryEnabled)
6224 {
6225 // Starting with Android 11 heap pointers get a tag which is stripped by the POSIX mprotect
6226 // callback. We need to add this tag manually to the untagged pointer in order to determine
6227 // the corresponding page.
6228 // See: https://source.android.com/docs/security/test/tagged-pointers
6229 // TODO(http://anglebug.com/7402): Determine when heap pointer tagging is not enabled.
6230 constexpr unsigned long long POINTER_TAG = 0xb400000000000000;
6231 unsigned long long taggedAddress = address | POINTER_TAG;
6232 page = static_cast<size_t>(taggedAddress / mPageSize);
6233 }
6234 else
6235 {
6236 // VMA allocated memory pointers are not tagged.
6237 page = address / mPageSize;
6238 }
6239 #else
6240 size_t page = address / mPageSize;
6241 #endif
6242
6243 for (const auto &pair : mBuffers)
6244 {
6245 std::shared_ptr<CoherentBuffer> buffer = pair.second;
6246 size_t relativePage;
6247 if (buffer->contains(page, &relativePage))
6248 {
6249 foundPages.insert(std::make_pair(buffer, relativePage));
6250 }
6251 }
6252
6253 return foundPages;
6254 }
6255
isDirty(gl::BufferID id)6256 bool CoherentBufferTracker::isDirty(gl::BufferID id)
6257 {
6258 return mBuffers[id.value]->isDirty();
6259 }
6260
enable()6261 void CoherentBufferTracker::enable()
6262 {
6263 if (mEnabled)
6264 {
6265 return;
6266 }
6267
6268 PageFaultCallback callback = [this](uintptr_t address) { return handleWrite(address); };
6269
6270 // This needs to be initialized after canProtectDirectly ran and can only be initialized once.
6271 if (!mPageFaultHandler)
6272 {
6273 mPageFaultHandler = std::unique_ptr<PageFaultHandler>(CreatePageFaultHandler(callback));
6274 }
6275
6276 bool ret = mPageFaultHandler->enable();
6277 if (ret)
6278 {
6279 mEnabled = true;
6280 }
6281 else
6282 {
6283 ERR() << "Could not enable page fault handler.";
6284 }
6285 }
6286
haveBuffer(gl::BufferID id)6287 bool CoherentBufferTracker::haveBuffer(gl::BufferID id)
6288 {
6289 return mBuffers.find(id.value) != mBuffers.end();
6290 }
6291
onEndFrame()6292 void CoherentBufferTracker::onEndFrame()
6293 {
6294 std::lock_guard<std::mutex> lock(mMutex);
6295
6296 if (!mEnabled)
6297 {
6298 return;
6299 }
6300
6301 // Remove protection from all buffers
6302 for (const auto &pair : mBuffers)
6303 {
6304 std::shared_ptr<CoherentBuffer> buffer = pair.second;
6305 buffer->removeProtection(PageSharingType::NoneShared);
6306 }
6307
6308 disable();
6309 }
6310
disable()6311 void CoherentBufferTracker::disable()
6312 {
6313 if (!mEnabled)
6314 {
6315 return;
6316 }
6317
6318 if (mPageFaultHandler->disable())
6319 {
6320 mEnabled = false;
6321 }
6322 else
6323 {
6324 ERR() << "Could not disable page fault handler.";
6325 }
6326
6327 if (mShadowMemoryEnabled && mBuffers.size() > 0)
6328 {
6329 WARN() << "Disabling coherent buffer tracking while leaving shadow memory without "
6330 "synchronization. Expect rendering artifacts after capture ends.";
6331 }
6332 }
6333
addBuffer(gl::BufferID id,uintptr_t start,size_t size)6334 uintptr_t CoherentBufferTracker::addBuffer(gl::BufferID id, uintptr_t start, size_t size)
6335 {
6336 std::lock_guard<std::mutex> lock(mMutex);
6337
6338 if (haveBuffer(id))
6339 {
6340 auto buffer = mBuffers[id.value];
6341 return buffer->getRange().start;
6342 }
6343
6344 auto buffer = std::make_shared<CoherentBuffer>(start, size, mPageSize, mShadowMemoryEnabled);
6345 uintptr_t realOrShadowStart = buffer->getRange().start;
6346
6347 mBuffers.insert(std::make_pair(id.value, std::move(buffer)));
6348
6349 return realOrShadowStart;
6350 }
6351
maybeUpdateShadowMemory()6352 void CoherentBufferTracker::maybeUpdateShadowMemory()
6353 {
6354 for (const auto &pair : mBuffers)
6355 {
6356 std::shared_ptr<CoherentBuffer> cb = pair.second;
6357 if (cb->isShadowDirty())
6358 {
6359 cb->removeProtection(PageSharingType::NoneShared);
6360 cb->updateShadowMemory();
6361 cb->protectAll();
6362 }
6363 }
6364 }
6365
markAllShadowDirty()6366 void CoherentBufferTracker::markAllShadowDirty()
6367 {
6368 for (const auto &pair : mBuffers)
6369 {
6370 std::shared_ptr<CoherentBuffer> cb = pair.second;
6371 cb->markShadowDirty();
6372 }
6373 }
6374
doesBufferSharePage(gl::BufferID id)6375 PageSharingType CoherentBufferTracker::doesBufferSharePage(gl::BufferID id)
6376 {
6377 bool firstPageShared = false;
6378 bool lastPageShared = false;
6379
6380 std::shared_ptr<CoherentBuffer> buffer = mBuffers[id.value];
6381
6382 AddressRange range = buffer->getRange();
6383
6384 size_t firstPage = range.start / mPageSize;
6385 size_t lastPage = range.end() / mPageSize;
6386
6387 for (const auto &pair : mBuffers)
6388 {
6389 gl::BufferID otherId = {pair.first};
6390 if (otherId != id)
6391 {
6392 std::shared_ptr<CoherentBuffer> otherBuffer = pair.second;
6393 size_t relativePage;
6394 if (otherBuffer->contains(firstPage, &relativePage))
6395 {
6396 firstPageShared = true;
6397 }
6398 else if (otherBuffer->contains(lastPage, &relativePage))
6399 {
6400 lastPageShared = true;
6401 }
6402 }
6403 }
6404
6405 if (firstPageShared && !lastPageShared)
6406 {
6407 return PageSharingType::FirstShared;
6408 }
6409 else if (!firstPageShared && lastPageShared)
6410 {
6411 return PageSharingType::LastShared;
6412 }
6413 else if (firstPageShared && lastPageShared)
6414 {
6415 return PageSharingType::FirstAndLastShared;
6416 }
6417 else
6418 {
6419 return PageSharingType::NoneShared;
6420 }
6421 }
6422
removeBuffer(gl::BufferID id)6423 void CoherentBufferTracker::removeBuffer(gl::BufferID id)
6424 {
6425 std::lock_guard<std::mutex> lock(mMutex);
6426
6427 if (!haveBuffer(id))
6428 {
6429 return;
6430 }
6431
6432 // Synchronize graphics buffer memory before the buffer is removed from the tracker.
6433 if (mShadowMemoryEnabled)
6434 {
6435 mBuffers[id.value]->updateBufferMemory();
6436 }
6437
6438 // If the buffer shares pages with other tracked buffers,
6439 // don't unprotect the overlapping pages.
6440 PageSharingType sharingType = doesBufferSharePage(id);
6441 mBuffers[id.value]->removeProtection(sharingType);
6442 mBuffers.erase(id.value);
6443 }
6444
maybeGetShadowMemoryPointer(gl::Buffer * buffer,GLsizeiptr length,GLbitfield access)6445 void *FrameCaptureShared::maybeGetShadowMemoryPointer(gl::Buffer *buffer,
6446 GLsizeiptr length,
6447 GLbitfield access)
6448 {
6449 if (!(access & GL_MAP_COHERENT_BIT_EXT) || !mCoherentBufferTracker.isShadowMemoryEnabled())
6450 {
6451 return buffer->getMapPointer();
6452 }
6453
6454 mCoherentBufferTracker.enable();
6455 uintptr_t realMapPointer = reinterpret_cast<uintptr_t>(buffer->getMapPointer());
6456 return (void *)mCoherentBufferTracker.addBuffer(buffer->id(), realMapPointer, length);
6457 }
6458
determineMemoryProtectionSupport(gl::Context * context)6459 void FrameCaptureShared::determineMemoryProtectionSupport(gl::Context *context)
6460 {
6461 // Skip this test if shadow memory was force enabled or shadow memory requirement was detected
6462 // previously
6463 if (mCoherentBufferTracker.isShadowMemoryEnabled())
6464 {
6465 return;
6466 }
6467
6468 // These known devices must use shadow memory
6469 HashMap<std::string, std::vector<std::string>> denyList = {
6470 {"Google", {"Pixel 6", "Pixel 6 Pro", "Pixel 6a", "Pixel 7", "Pixel 7 Pro"}},
6471 };
6472
6473 angle::SystemInfo info;
6474 angle::GetSystemInfo(&info);
6475 bool isDeviceDenyListed = false;
6476
6477 if (denyList.find(info.machineManufacturer) != denyList.end())
6478 {
6479 const std::vector<std::string> &models = denyList[info.machineManufacturer];
6480 isDeviceDenyListed =
6481 std::find(models.begin(), models.end(), info.machineModelName) != models.end();
6482 }
6483
6484 if (isDeviceDenyListed)
6485 {
6486 WARN() << "Direct memory protection not possible on deny listed device '"
6487 << info.machineModelName
6488 << "', enabling shadow memory for coherent buffer tracking.";
6489 mCoherentBufferTracker.enableShadowMemory();
6490 }
6491 else
6492 {
6493 // Device is not on deny listed. Run a test if we actually can protect directly. Do this
6494 // only on assertion enabled builds.
6495 ASSERT(mCoherentBufferTracker.canProtectDirectly(context));
6496 }
6497 }
6498
trackBufferMapping(const gl::Context * context,CallCapture * call,gl::BufferID id,gl::Buffer * buffer,GLintptr offset,GLsizeiptr length,bool writable,bool coherent)6499 void FrameCaptureShared::trackBufferMapping(const gl::Context *context,
6500 CallCapture *call,
6501 gl::BufferID id,
6502 gl::Buffer *buffer,
6503 GLintptr offset,
6504 GLsizeiptr length,
6505 bool writable,
6506 bool coherent)
6507 {
6508 // Track that the buffer was mapped
6509 mResourceTracker.setBufferMapped(context->id(), id.value);
6510
6511 if (writable)
6512 {
6513 // If this buffer was mapped writable, we don't have any visibility into what
6514 // happens to it. Therefore, remember the details about it, and we'll read it back
6515 // on Unmap to repopulate it during replay.
6516 mBufferDataMap[id] = std::make_pair(offset, length);
6517
6518 // Track that this buffer was potentially modified
6519 mResourceTracker.getTrackedResource(context->id(), ResourceIDType::Buffer)
6520 .setModifiedResource(id.value);
6521
6522 // Track coherent buffer
6523 // Check if capture is active to not initialize the coherent buffer tracker on the
6524 // first coherent glMapBufferRange call.
6525 if (coherent && isCaptureActive())
6526 {
6527 mCoherentBufferTracker.enable();
6528 // When not using shadow memory, adding buffers to the tracking happens here instead of
6529 // during mapping
6530 if (!mCoherentBufferTracker.isShadowMemoryEnabled())
6531 {
6532 uintptr_t data = reinterpret_cast<uintptr_t>(buffer->getMapPointer());
6533 mCoherentBufferTracker.addBuffer(id, data, length);
6534 }
6535 }
6536 }
6537 }
6538
trackTextureUpdate(const gl::Context * context,const CallCapture & call)6539 void FrameCaptureShared::trackTextureUpdate(const gl::Context *context, const CallCapture &call)
6540 {
6541 int index = 0;
6542 std::string paramName = "targetPacked";
6543 ParamType paramType = ParamType::TTextureTarget;
6544
6545 // Some calls provide the textureID directly
6546 // For the rest, look it up based on the currently bound texture
6547 switch (call.entryPoint)
6548 {
6549 case EntryPoint::GLCompressedCopyTextureCHROMIUM:
6550 index = 1;
6551 paramName = "destIdPacked";
6552 paramType = ParamType::TTextureID;
6553 break;
6554 case EntryPoint::GLCopyTextureCHROMIUM:
6555 case EntryPoint::GLCopySubTextureCHROMIUM:
6556 case EntryPoint::GLCopyTexture3DANGLE:
6557 index = 3;
6558 paramName = "destIdPacked";
6559 paramType = ParamType::TTextureID;
6560 break;
6561 case EntryPoint::GLCopyImageSubData:
6562 case EntryPoint::GLCopyImageSubDataEXT:
6563 case EntryPoint::GLCopyImageSubDataOES:
6564 index = 7;
6565 paramName = "dstTarget";
6566 paramType = ParamType::TGLenum;
6567 break;
6568 default:
6569 break;
6570 }
6571
6572 GLuint id = 0;
6573 switch (paramType)
6574 {
6575 case ParamType::TTextureTarget:
6576 {
6577 gl::TextureTarget targetPacked =
6578 call.params.getParam(paramName.c_str(), ParamType::TTextureTarget, index)
6579 .value.TextureTargetVal;
6580 gl::TextureType textureType = gl::TextureTargetToType(targetPacked);
6581 gl::Texture *texture = context->getState().getTargetTexture(textureType);
6582 id = texture->id().value;
6583 break;
6584 }
6585 case ParamType::TTextureID:
6586 {
6587 gl::TextureID destIDPacked =
6588 call.params.getParam(paramName.c_str(), ParamType::TTextureID, index)
6589 .value.TextureIDVal;
6590 id = destIDPacked.value;
6591 break;
6592 }
6593 case ParamType::TGLenum:
6594 {
6595 GLenum target =
6596 call.params.getParam(paramName.c_str(), ParamType::TGLenum, index).value.GLenumVal;
6597
6598 if (target == GL_TEXTURE_CUBE_MAP)
6599 {
6600 // CopyImageSubData doesn't support cube faces, but PackedParams requires one
6601 target = GL_TEXTURE_CUBE_MAP_POSITIVE_X;
6602 }
6603
6604 gl::TextureTarget targetPacked = gl::PackParam<gl::TextureTarget>(target);
6605 gl::TextureType textureType = gl::TextureTargetToType(targetPacked);
6606 gl::Texture *texture = context->getState().getTargetTexture(textureType);
6607 id = texture->id().value;
6608 break;
6609 }
6610 default:
6611 ERR() << "Unhandled paramType= " << static_cast<int>(paramType);
6612 UNREACHABLE();
6613 break;
6614 }
6615
6616 // Mark it as modified
6617 mResourceTracker.getTrackedResource(context->id(), ResourceIDType::Texture)
6618 .setModifiedResource(id);
6619 }
6620
trackDefaultUniformUpdate(const gl::Context * context,const CallCapture & call)6621 void FrameCaptureShared::trackDefaultUniformUpdate(const gl::Context *context,
6622 const CallCapture &call)
6623 {
6624 DefaultUniformType defaultUniformType = GetDefaultUniformType(call);
6625
6626 GLuint programID = 0;
6627 int location = 0;
6628
6629 // We track default uniform updates by program and location, so look them up in parameters
6630 if (defaultUniformType == DefaultUniformType::CurrentProgram)
6631 {
6632 programID = context->getActiveLinkedProgram()->id().value;
6633
6634 location = call.params.getParam("locationPacked", ParamType::TUniformLocation, 0)
6635 .value.UniformLocationVal.value;
6636 }
6637 else
6638 {
6639 ASSERT(defaultUniformType == DefaultUniformType::SpecifiedProgram);
6640
6641 programID = call.params.getParam("programPacked", ParamType::TShaderProgramID, 0)
6642 .value.ShaderProgramIDVal.value;
6643
6644 location = call.params.getParam("locationPacked", ParamType::TUniformLocation, 1)
6645 .value.UniformLocationVal.value;
6646 }
6647
6648 const TrackedResource &trackedShaderProgram =
6649 mResourceTracker.getTrackedResource(context->id(), ResourceIDType::ShaderProgram);
6650 const ResourceSet &startingPrograms = trackedShaderProgram.getStartingResources();
6651 const ResourceSet &programsToRegen = trackedShaderProgram.getResourcesToRegen();
6652
6653 // If this program was in our starting set, track its uniform updates. Unless it was deleted,
6654 // then its uniforms will all be regenned along wih with the program.
6655 if (startingPrograms.find(programID) != startingPrograms.end() &&
6656 programsToRegen.find(programID) == programsToRegen.end())
6657 {
6658 // Track that we need to set this default uniform value again
6659 mResourceTracker.setModifiedDefaultUniform({programID}, {location});
6660 }
6661 }
6662
trackVertexArrayUpdate(const gl::Context * context,const CallCapture & call)6663 void FrameCaptureShared::trackVertexArrayUpdate(const gl::Context *context, const CallCapture &call)
6664 {
6665 // Look up the currently bound vertex array
6666 gl::VertexArrayID id = context->getState().getVertexArray()->id();
6667
6668 // Mark it as modified
6669 mResourceTracker.getTrackedResource(context->id(), ResourceIDType::VertexArray)
6670 .setModifiedResource(id.value);
6671 }
6672
updateCopyImageSubData(CallCapture & call)6673 void FrameCaptureShared::updateCopyImageSubData(CallCapture &call)
6674 {
6675 // This call modifies srcName and dstName to no longer be object IDs (GLuint), but actual
6676 // packed types that can remapped using gTextureMap and gRenderbufferMap
6677
6678 GLint srcName = call.params.getParam("srcName", ParamType::TGLuint, 0).value.GLuintVal;
6679 GLenum srcTarget = call.params.getParam("srcTarget", ParamType::TGLenum, 1).value.GLenumVal;
6680 switch (srcTarget)
6681 {
6682 case GL_RENDERBUFFER:
6683 {
6684 // Convert the GLuint to RenderbufferID
6685 gl::RenderbufferID srcRenderbufferID = {static_cast<GLuint>(srcName)};
6686 call.params.setValueParamAtIndex("srcName", ParamType::TRenderbufferID,
6687 srcRenderbufferID, 0);
6688 break;
6689 }
6690 case GL_TEXTURE_2D:
6691 case GL_TEXTURE_2D_ARRAY:
6692 case GL_TEXTURE_3D:
6693 case GL_TEXTURE_CUBE_MAP:
6694 {
6695 // Convert the GLuint to TextureID
6696 gl::TextureID srcTextureID = {static_cast<GLuint>(srcName)};
6697 call.params.setValueParamAtIndex("srcName", ParamType::TTextureID, srcTextureID, 0);
6698 break;
6699 }
6700 default:
6701 ERR() << "Unhandled srcTarget = " << srcTarget;
6702 UNREACHABLE();
6703 break;
6704 }
6705
6706 // Change dstName to the appropriate type based on dstTarget
6707 GLint dstName = call.params.getParam("dstName", ParamType::TGLuint, 6).value.GLuintVal;
6708 GLenum dstTarget = call.params.getParam("dstTarget", ParamType::TGLenum, 7).value.GLenumVal;
6709 switch (dstTarget)
6710 {
6711 case GL_RENDERBUFFER:
6712 {
6713 // Convert the GLuint to RenderbufferID
6714 gl::RenderbufferID dstRenderbufferID = {static_cast<GLuint>(dstName)};
6715 call.params.setValueParamAtIndex("dstName", ParamType::TRenderbufferID,
6716 dstRenderbufferID, 6);
6717 break;
6718 }
6719 case GL_TEXTURE_2D:
6720 case GL_TEXTURE_2D_ARRAY:
6721 case GL_TEXTURE_3D:
6722 case GL_TEXTURE_CUBE_MAP:
6723 {
6724 // Convert the GLuint to TextureID
6725 gl::TextureID dstTextureID = {static_cast<GLuint>(dstName)};
6726 call.params.setValueParamAtIndex("dstName", ParamType::TTextureID, dstTextureID, 6);
6727 break;
6728 }
6729 default:
6730 ERR() << "Unhandled dstTarget = " << dstTarget;
6731 UNREACHABLE();
6732 break;
6733 }
6734 }
6735
overrideProgramBinary(const gl::Context * context,CallCapture & inCall,std::vector<CallCapture> & outCalls)6736 void FrameCaptureShared::overrideProgramBinary(const gl::Context *context,
6737 CallCapture &inCall,
6738 std::vector<CallCapture> &outCalls)
6739 {
6740 // Program binaries are inherently non-portable, even between two ANGLE builds.
6741 // If an application is using glProgramBinary in the middle of a trace, we need to replace
6742 // those calls with an equivalent sequence of portable calls.
6743 //
6744 // For example, here is a sequence an app could use for glProgramBinary:
6745 //
6746 // gShaderProgramMap[42] = glCreateProgram();
6747 // glProgramBinary(gShaderProgramMap[42], GL_PROGRAM_BINARY_ANGLE, gBinaryData[x], 1000);
6748 // glGetProgramiv(gShaderProgramMap[42], GL_LINK_STATUS, gReadBuffer);
6749 // glGetProgramiv(gShaderProgramMap[42], GL_PROGRAM_BINARY_LENGTH, gReadBuffer);
6750 //
6751 // With this override, the glProgramBinary call will be replaced like so:
6752 //
6753 // gShaderProgramMap[42] = glCreateProgram();
6754 // === Begin override ===
6755 // gShaderProgramMap[43] = glCreateShader(GL_VERTEX_SHADER);
6756 // glShaderSource(gShaderProgramMap[43], 1, string_0, &gBinaryData[100]);
6757 // glCompileShader(gShaderProgramMap[43]);
6758 // glAttachShader(gShaderProgramMap[42], gShaderProgramMap[43]);
6759 // glDeleteShader(gShaderProgramMap[43]);
6760 // gShaderProgramMap[43] = glCreateShader(GL_FRAGMENT_SHADER);
6761 // glShaderSource(gShaderProgramMap[43], 1, string_1, &gBinaryData[200]);
6762 // glCompileShader(gShaderProgramMap[43]);
6763 // glAttachShader(gShaderProgramMap[42], gShaderProgramMap[43]);
6764 // glDeleteShader(gShaderProgramMap[43]);
6765 // glBindAttribLocation(gShaderProgramMap[42], 0, "attrib1");
6766 // glBindAttribLocation(gShaderProgramMap[42], 1, "attrib2");
6767 // glLinkProgram(gShaderProgramMap[42]);
6768 // UpdateUniformLocation(gShaderProgramMap[42], "foo", 0, 20);
6769 // UpdateUniformLocation(gShaderProgramMap[42], "bar", 72, 1);
6770 // glUseProgram(gShaderProgramMap[42]);
6771 // UpdateCurrentProgram(gShaderProgramMap[42]);
6772 // glUniform4fv(gUniformLocations[gCurrentProgram][0], 20, &gBinaryData[300]);
6773 // glUniform1iv(gUniformLocations[gCurrentProgram][72], 1, &gBinaryData[400]);
6774 // === End override ===
6775 // glGetProgramiv(gShaderProgramMap[42], GL_LINK_STATUS, gReadBuffer);
6776 // glGetProgramiv(gShaderProgramMap[42], GL_PROGRAM_BINARY_LENGTH, gReadBuffer);
6777 //
6778 // To facilitate this override, we are serializing each shader stage source into the binary
6779 // itself. See Program::serialize and Program::deserialize. Once extracted from the binary,
6780 // they will be available via getProgramSources.
6781
6782 gl::ShaderProgramID id = inCall.params.getParam("programPacked", ParamType::TShaderProgramID, 0)
6783 .value.ShaderProgramIDVal;
6784
6785 gl::Program *program = context->getProgramResolveLink(id);
6786 ASSERT(program);
6787
6788 mResourceTracker.onShaderProgramAccess(id);
6789 gl::ShaderProgramID tempShaderStartID = {mResourceTracker.getMaxShaderPrograms()};
6790 GenerateLinkedProgram(context, context->getState(), &mResourceTracker, &outCalls, program, id,
6791 tempShaderStartID, getProgramSources(id));
6792 }
6793
captureCustomMapBufferFromContext(const gl::Context * context,const char * entryPointName,CallCapture & call,std::vector<CallCapture> & callsOut)6794 void FrameCaptureShared::captureCustomMapBufferFromContext(const gl::Context *context,
6795 const char *entryPointName,
6796 CallCapture &call,
6797 std::vector<CallCapture> &callsOut)
6798 {
6799 gl::BufferBinding binding =
6800 call.params.getParam("targetPacked", ParamType::TBufferBinding, 0).value.BufferBindingVal;
6801 gl::Buffer *buffer = context->getState().getTargetBuffer(binding);
6802
6803 if (call.entryPoint == EntryPoint::GLMapBufferRange ||
6804 call.entryPoint == EntryPoint::GLMapBufferRangeEXT)
6805 {
6806 GLintptr offset = call.params.getParam("offset", ParamType::TGLintptr, 1).value.GLintptrVal;
6807 GLsizeiptr length =
6808 call.params.getParam("length", ParamType::TGLsizeiptr, 2).value.GLsizeiptrVal;
6809 GLbitfield access =
6810 call.params.getParam("access", ParamType::TGLbitfield, 3).value.GLbitfieldVal;
6811
6812 trackBufferMapping(context, &call, buffer->id(), buffer, offset, length,
6813 access & GL_MAP_WRITE_BIT, access & GL_MAP_COHERENT_BIT_EXT);
6814 }
6815 else
6816 {
6817 ASSERT(call.entryPoint == EntryPoint::GLMapBufferOES);
6818 GLenum access = call.params.getParam("access", ParamType::TGLenum, 1).value.GLenumVal;
6819 bool writeAccess =
6820 (access == GL_WRITE_ONLY_OES || access == GL_WRITE_ONLY || access == GL_READ_WRITE);
6821 trackBufferMapping(context, &call, buffer->id(), buffer, 0,
6822 static_cast<GLsizeiptr>(buffer->getSize()), writeAccess, false);
6823 }
6824
6825 CaptureCustomMapBuffer(entryPointName, call, callsOut, buffer->id());
6826 }
6827
maybeOverrideEntryPoint(const gl::Context * context,CallCapture & inCall,std::vector<CallCapture> & outCalls)6828 void FrameCaptureShared::maybeOverrideEntryPoint(const gl::Context *context,
6829 CallCapture &inCall,
6830 std::vector<CallCapture> &outCalls)
6831 {
6832 switch (inCall.entryPoint)
6833 {
6834 case EntryPoint::GLCopyImageSubData:
6835 case EntryPoint::GLCopyImageSubDataEXT:
6836 case EntryPoint::GLCopyImageSubDataOES:
6837 {
6838 // We must look at the src and dst target types to determine which remap table to use
6839 updateCopyImageSubData(inCall);
6840 outCalls.emplace_back(std::move(inCall));
6841 break;
6842 }
6843 case EntryPoint::GLProgramBinary:
6844 case EntryPoint::GLProgramBinaryOES:
6845 {
6846 // Binary formats are not portable at all, so replace the calls with full linking
6847 // sequence
6848 overrideProgramBinary(context, inCall, outCalls);
6849 break;
6850 }
6851 case EntryPoint::GLUniformBlockBinding:
6852 {
6853 CaptureCustomUniformBlockBinding(inCall, outCalls);
6854 break;
6855 }
6856 case EntryPoint::GLMapBufferRange:
6857 {
6858 captureCustomMapBufferFromContext(context, "MapBufferRange", inCall, outCalls);
6859 break;
6860 }
6861 case EntryPoint::GLMapBufferRangeEXT:
6862 {
6863 captureCustomMapBufferFromContext(context, "MapBufferRangeEXT", inCall, outCalls);
6864 break;
6865 }
6866 case EntryPoint::GLMapBuffer:
6867 {
6868 // Currently desktop GL is not implemented.
6869 UNREACHABLE();
6870 break;
6871 }
6872 case EntryPoint::GLMapBufferOES:
6873 {
6874 captureCustomMapBufferFromContext(context, "MapBufferOES", inCall, outCalls);
6875 break;
6876 }
6877 case EntryPoint::GLCreateShader:
6878 {
6879 CaptureCustomShaderProgram("CreateShader", inCall, outCalls);
6880 break;
6881 }
6882 case EntryPoint::GLCreateProgram:
6883 {
6884 CaptureCustomShaderProgram("CreateProgram", inCall, outCalls);
6885 break;
6886 }
6887 case EntryPoint::GLCreateShaderProgramv:
6888 {
6889 CaptureCustomShaderProgram("CreateShaderProgramv", inCall, outCalls);
6890 break;
6891 }
6892 case EntryPoint::GLFenceSync:
6893 {
6894 CaptureCustomFenceSync(inCall, outCalls);
6895 break;
6896 }
6897 case EntryPoint::EGLCreateImage:
6898 {
6899 CaptureCustomCreateEGLImage("CreateEGLImage", inCall, outCalls);
6900 break;
6901 }
6902 case EntryPoint::EGLCreateImageKHR:
6903 {
6904 CaptureCustomCreateEGLImage("CreateEGLImageKHR", inCall, outCalls);
6905 break;
6906 }
6907 case EntryPoint::EGLCreateSync:
6908 {
6909 CaptureCustomCreateEGLSync("CreateEGLSync", inCall, outCalls);
6910 break;
6911 }
6912 case EntryPoint::EGLCreateSyncKHR:
6913 {
6914 CaptureCustomCreateEGLSync("CreateEGLSyncKHR", inCall, outCalls);
6915 break;
6916 }
6917 case EntryPoint::EGLCreatePbufferSurface:
6918 {
6919 CaptureCustomCreatePbufferSurface(inCall, outCalls);
6920 break;
6921 }
6922 case EntryPoint::EGLCreateNativeClientBufferANDROID:
6923 {
6924 CaptureCustomCreateNativeClientbuffer(inCall, outCalls);
6925 break;
6926 }
6927
6928 default:
6929 {
6930 // Pass the single call through
6931 outCalls.emplace_back(std::move(inCall));
6932 break;
6933 }
6934 }
6935 }
6936
maybeCaptureCoherentBuffers(const gl::Context * context)6937 void FrameCaptureShared::maybeCaptureCoherentBuffers(const gl::Context *context)
6938 {
6939 if (!isCaptureActive())
6940 {
6941 return;
6942 }
6943
6944 std::lock_guard<std::mutex> lock(mCoherentBufferTracker.mMutex);
6945
6946 for (const auto &pair : mCoherentBufferTracker.mBuffers)
6947 {
6948 gl::BufferID id = {pair.first};
6949 if (mCoherentBufferTracker.isDirty(id))
6950 {
6951 captureCoherentBufferSnapshot(context, id);
6952 }
6953 }
6954 }
6955
maybeCaptureDrawArraysClientData(const gl::Context * context,CallCapture & call,size_t instanceCount)6956 void FrameCaptureShared::maybeCaptureDrawArraysClientData(const gl::Context *context,
6957 CallCapture &call,
6958 size_t instanceCount)
6959 {
6960 if (!context->getStateCache().hasAnyActiveClientAttrib())
6961 {
6962 return;
6963 }
6964
6965 // Get counts from paramBuffer.
6966 GLint firstVertex =
6967 call.params.getParamFlexName("first", "start", ParamType::TGLint, 1).value.GLintVal;
6968 GLsizei drawCount = call.params.getParam("count", ParamType::TGLsizei, 2).value.GLsizeiVal;
6969 captureClientArraySnapshot(context, firstVertex + drawCount, instanceCount);
6970 }
6971
maybeCaptureDrawElementsClientData(const gl::Context * context,CallCapture & call,size_t instanceCount)6972 void FrameCaptureShared::maybeCaptureDrawElementsClientData(const gl::Context *context,
6973 CallCapture &call,
6974 size_t instanceCount)
6975 {
6976 if (!context->getStateCache().hasAnyActiveClientAttrib())
6977 {
6978 return;
6979 }
6980
6981 // if the count is zero then the index evaluation is not valid and we wouldn't be drawing
6982 // anything anyway, so skip capturing
6983 GLsizei count = call.params.getParam("count", ParamType::TGLsizei, 1).value.GLsizeiVal;
6984 if (count == 0)
6985 {
6986 return;
6987 }
6988
6989 gl::DrawElementsType drawElementsType =
6990 call.params.getParam("typePacked", ParamType::TDrawElementsType, 2)
6991 .value.DrawElementsTypeVal;
6992 const void *indices =
6993 call.params.getParam("indices", ParamType::TvoidConstPointer, 3).value.voidConstPointerVal;
6994
6995 gl::IndexRange indexRange;
6996
6997 bool restart = context->getState().isPrimitiveRestartEnabled();
6998
6999 gl::Buffer *elementArrayBuffer = context->getState().getVertexArray()->getElementArrayBuffer();
7000 if (elementArrayBuffer)
7001 {
7002 size_t offset = reinterpret_cast<size_t>(indices);
7003 (void)elementArrayBuffer->getIndexRange(context, drawElementsType, offset, count, restart,
7004 &indexRange);
7005 }
7006 else
7007 {
7008 ASSERT(indices);
7009 indexRange = gl::ComputeIndexRange(drawElementsType, indices, count, restart);
7010 }
7011
7012 // index starts from 0
7013 captureClientArraySnapshot(context, indexRange.end + 1, instanceCount);
7014 }
7015
7016 template <typename AttribT, typename FactoryT>
CreateEGLImagePreCallUpdate(const CallCapture & call,ResourceTracker & resourceTracker,ParamType paramType,FactoryT factory)7017 void CreateEGLImagePreCallUpdate(const CallCapture &call,
7018 ResourceTracker &resourceTracker,
7019 ParamType paramType,
7020 FactoryT factory)
7021 {
7022 EGLImage image = call.params.getReturnValue().value.EGLImageVal;
7023 const ParamCapture ¶m = call.params.getParam("attrib_list", paramType, 4);
7024 const AttribT *attribs =
7025 param.data.empty() ? nullptr : reinterpret_cast<const AttribT *>(param.data[0].data());
7026 egl::AttributeMap attributeMap = factory(attribs);
7027 attributeMap.initializeWithoutValidation();
7028 resourceTracker.getImageToAttribTable().insert(
7029 std::pair<EGLImage, egl::AttributeMap>(image, attributeMap));
7030 }
7031
maybeCapturePreCallUpdates(const gl::Context * context,CallCapture & call,std::vector<CallCapture> * shareGroupSetupCalls,ResourceIDToSetupCallsMap * resourceIDToSetupCalls)7032 void FrameCaptureShared::maybeCapturePreCallUpdates(
7033 const gl::Context *context,
7034 CallCapture &call,
7035 std::vector<CallCapture> *shareGroupSetupCalls,
7036 ResourceIDToSetupCallsMap *resourceIDToSetupCalls)
7037 {
7038 switch (call.entryPoint)
7039 {
7040 case EntryPoint::GLVertexAttribPointer:
7041 case EntryPoint::GLVertexPointer:
7042 case EntryPoint::GLColorPointer:
7043 case EntryPoint::GLTexCoordPointer:
7044 case EntryPoint::GLNormalPointer:
7045 case EntryPoint::GLPointSizePointerOES:
7046 {
7047 // Get array location
7048 GLuint index = 0;
7049 if (call.entryPoint == EntryPoint::GLVertexAttribPointer)
7050 {
7051 index = call.params.getParam("index", ParamType::TGLuint, 0).value.GLuintVal;
7052 }
7053 else
7054 {
7055 gl::ClientVertexArrayType type;
7056 switch (call.entryPoint)
7057 {
7058 case EntryPoint::GLVertexPointer:
7059 type = gl::ClientVertexArrayType::Vertex;
7060 break;
7061 case EntryPoint::GLColorPointer:
7062 type = gl::ClientVertexArrayType::Color;
7063 break;
7064 case EntryPoint::GLTexCoordPointer:
7065 type = gl::ClientVertexArrayType::TextureCoord;
7066 break;
7067 case EntryPoint::GLNormalPointer:
7068 type = gl::ClientVertexArrayType::Normal;
7069 break;
7070 case EntryPoint::GLPointSizePointerOES:
7071 type = gl::ClientVertexArrayType::PointSize;
7072 break;
7073 default:
7074 UNREACHABLE();
7075 type = gl::ClientVertexArrayType::InvalidEnum;
7076 }
7077 index = gl::GLES1Renderer::VertexArrayIndex(type, context->getState().gles1());
7078 }
7079
7080 if (call.params.hasClientArrayData())
7081 {
7082 mClientVertexArrayMap[index] = static_cast<int>(mFrameCalls.size());
7083 }
7084 else
7085 {
7086 mClientVertexArrayMap[index] = -1;
7087 }
7088 break;
7089 }
7090
7091 case EntryPoint::GLGenFramebuffers:
7092 case EntryPoint::GLGenFramebuffersOES:
7093 {
7094 GLsizei count = call.params.getParam("n", ParamType::TGLsizei, 0).value.GLsizeiVal;
7095 const gl::FramebufferID *framebufferIDs =
7096 call.params.getParam("framebuffersPacked", ParamType::TFramebufferIDPointer, 1)
7097 .value.FramebufferIDPointerVal;
7098 for (GLsizei i = 0; i < count; i++)
7099 {
7100 handleGennedResource(context, framebufferIDs[i]);
7101 }
7102 break;
7103 }
7104
7105 case EntryPoint::GLBindFramebuffer:
7106 case EntryPoint::GLBindFramebufferOES:
7107 maybeGenResourceOnBind<gl::FramebufferID>(context, call);
7108 break;
7109
7110 case EntryPoint::GLGenRenderbuffers:
7111 case EntryPoint::GLGenRenderbuffersOES:
7112 {
7113 GLsizei count = call.params.getParam("n", ParamType::TGLsizei, 0).value.GLsizeiVal;
7114 const gl::RenderbufferID *renderbufferIDs =
7115 call.params.getParam("renderbuffersPacked", ParamType::TRenderbufferIDPointer, 1)
7116 .value.RenderbufferIDPointerVal;
7117 for (GLsizei i = 0; i < count; i++)
7118 {
7119 handleGennedResource(context, renderbufferIDs[i]);
7120 }
7121 break;
7122 }
7123
7124 case EntryPoint::GLBindRenderbuffer:
7125 case EntryPoint::GLBindRenderbufferOES:
7126 maybeGenResourceOnBind<gl::RenderbufferID>(context, call);
7127 break;
7128
7129 case EntryPoint::GLDeleteRenderbuffers:
7130 case EntryPoint::GLDeleteRenderbuffersOES:
7131 {
7132 // Look up how many renderbuffers are being deleted
7133 GLsizei n = call.params.getParam("n", ParamType::TGLsizei, 0).value.GLsizeiVal;
7134
7135 // Look up the pointer to list of renderbuffers
7136 const gl::RenderbufferID *renderbufferIDs =
7137 call.params
7138 .getParam("renderbuffersPacked", ParamType::TRenderbufferIDConstPointer, 1)
7139 .value.RenderbufferIDConstPointerVal;
7140
7141 // For each renderbuffer listed for deletion
7142 for (int32_t i = 0; i < n; ++i)
7143 {
7144 // If we're capturing, track what renderbuffers have been deleted
7145 handleDeletedResource(context, renderbufferIDs[i]);
7146 }
7147 break;
7148 }
7149
7150 case EntryPoint::GLGenTextures:
7151 {
7152 GLsizei count = call.params.getParam("n", ParamType::TGLsizei, 0).value.GLsizeiVal;
7153 const gl::TextureID *textureIDs =
7154 call.params.getParam("texturesPacked", ParamType::TTextureIDPointer, 1)
7155 .value.TextureIDPointerVal;
7156 for (GLsizei i = 0; i < count; i++)
7157 {
7158 // If we're capturing, track what new textures have been genned
7159 handleGennedResource(context, textureIDs[i]);
7160 }
7161 break;
7162 }
7163
7164 case EntryPoint::GLBindTexture:
7165 maybeGenResourceOnBind<gl::TextureID>(context, call);
7166 break;
7167
7168 case EntryPoint::GLDeleteBuffers:
7169 {
7170 GLsizei count = call.params.getParam("n", ParamType::TGLsizei, 0).value.GLsizeiVal;
7171 const gl::BufferID *bufferIDs =
7172 call.params.getParam("buffersPacked", ParamType::TBufferIDConstPointer, 1)
7173 .value.BufferIDConstPointerVal;
7174 for (GLsizei i = 0; i < count; i++)
7175 {
7176 // For each buffer being deleted, check our backup of data and remove it
7177 const auto &bufferDataInfo = mBufferDataMap.find(bufferIDs[i]);
7178 if (bufferDataInfo != mBufferDataMap.end())
7179 {
7180 mBufferDataMap.erase(bufferDataInfo);
7181 }
7182 // If we're capturing, track what buffers have been deleted
7183 handleDeletedResource(context, bufferIDs[i]);
7184 }
7185 break;
7186 }
7187
7188 case EntryPoint::GLGenBuffers:
7189 {
7190 GLsizei count = call.params.getParam("n", ParamType::TGLsizei, 0).value.GLsizeiVal;
7191 const gl::BufferID *bufferIDs =
7192 call.params.getParam("buffersPacked", ParamType::TBufferIDPointer, 1)
7193 .value.BufferIDPointerVal;
7194 for (GLsizei i = 0; i < count; i++)
7195 {
7196 handleGennedResource(context, bufferIDs[i]);
7197 }
7198 break;
7199 }
7200
7201 case EntryPoint::GLBindBuffer:
7202 maybeGenResourceOnBind<gl::BufferID>(context, call);
7203 break;
7204
7205 case EntryPoint::GLDeleteProgramPipelines:
7206 case EntryPoint::GLDeleteProgramPipelinesEXT:
7207 {
7208 GLsizei count = call.params.getParam("n", ParamType::TGLsizei, 0).value.GLsizeiVal;
7209 const gl::ProgramPipelineID *pipelineIDs =
7210 call.params
7211 .getParam("pipelinesPacked", ParamType::TProgramPipelineIDConstPointer, 1)
7212 .value.ProgramPipelineIDPointerVal;
7213 for (GLsizei i = 0; i < count; i++)
7214 {
7215 handleDeletedResource(context, pipelineIDs[i]);
7216 }
7217 break;
7218 }
7219
7220 case EntryPoint::GLGenProgramPipelines:
7221 case EntryPoint::GLGenProgramPipelinesEXT:
7222 {
7223 GLsizei count = call.params.getParam("n", ParamType::TGLsizei, 0).value.GLsizeiVal;
7224 const gl::ProgramPipelineID *pipelineIDs =
7225 call.params.getParam("pipelinesPacked", ParamType::TProgramPipelineIDPointer, 1)
7226 .value.ProgramPipelineIDPointerVal;
7227 for (GLsizei i = 0; i < count; i++)
7228 {
7229 handleGennedResource(context, pipelineIDs[i]);
7230 }
7231 break;
7232 }
7233
7234 case EntryPoint::GLDeleteSync:
7235 {
7236 gl::SyncID sync =
7237 call.params.getParam("syncPacked", ParamType::TSyncID, 0).value.SyncIDVal;
7238 FrameCaptureShared *frameCaptureShared =
7239 context->getShareGroup()->getFrameCaptureShared();
7240 // If we're capturing, track which fence sync has been deleted
7241 if (frameCaptureShared->isCaptureActive())
7242 {
7243 mResourceTracker.setDeletedFenceSync(sync);
7244 }
7245 break;
7246 }
7247
7248 case EntryPoint::GLDrawArrays:
7249 {
7250 maybeCaptureDrawArraysClientData(context, call, 1);
7251 maybeCaptureCoherentBuffers(context);
7252 break;
7253 }
7254
7255 case EntryPoint::GLDrawArraysInstanced:
7256 case EntryPoint::GLDrawArraysInstancedANGLE:
7257 case EntryPoint::GLDrawArraysInstancedEXT:
7258 {
7259 GLsizei instancecount =
7260 call.params.getParamFlexName("instancecount", "primcount", ParamType::TGLsizei, 3)
7261 .value.GLsizeiVal;
7262
7263 maybeCaptureDrawArraysClientData(context, call, instancecount);
7264 maybeCaptureCoherentBuffers(context);
7265 break;
7266 }
7267
7268 case EntryPoint::GLDrawElements:
7269 {
7270 maybeCaptureDrawElementsClientData(context, call, 1);
7271 maybeCaptureCoherentBuffers(context);
7272 break;
7273 }
7274
7275 case EntryPoint::GLDrawElementsInstanced:
7276 case EntryPoint::GLDrawElementsInstancedANGLE:
7277 case EntryPoint::GLDrawElementsInstancedEXT:
7278 {
7279 GLsizei instancecount =
7280 call.params.getParamFlexName("instancecount", "primcount", ParamType::TGLsizei, 4)
7281 .value.GLsizeiVal;
7282
7283 maybeCaptureDrawElementsClientData(context, call, instancecount);
7284 maybeCaptureCoherentBuffers(context);
7285 break;
7286 }
7287
7288 case EntryPoint::GLCreateShaderProgramv:
7289 {
7290 // Refresh the cached shader sources.
7291 // The command CreateShaderProgramv() creates a stand-alone program from an array of
7292 // null-terminated source code strings for a single shader type, so we need update the
7293 // Shader and Program sources, similar to GLCompileShader + GLLinkProgram handling.
7294 gl::ShaderProgramID programID = {call.params.getReturnValue().value.GLuintVal};
7295 const ParamCapture ¶mCapture =
7296 call.params.getParam("typePacked", ParamType::TShaderType, 0);
7297 const ParamCapture &lineCount = call.params.getParam("count", ParamType::TGLsizei, 1);
7298 const ParamCapture &strings =
7299 call.params.getParam("strings", ParamType::TGLcharConstPointerPointer, 2);
7300
7301 std::ostringstream sourceString;
7302 for (int i = 0; i < lineCount.value.GLsizeiVal; ++i)
7303 {
7304 sourceString << strings.value.GLcharConstPointerPointerVal[i];
7305 }
7306
7307 gl::ShaderType shaderType = paramCapture.value.ShaderTypeVal;
7308 ProgramSources source;
7309 source[shaderType] = sourceString.str();
7310 setProgramSources(programID, source);
7311 handleGennedResource(context, programID);
7312 mResourceTracker.setShaderProgramType(programID, ShaderProgramType::ProgramType);
7313 break;
7314 }
7315
7316 case EntryPoint::GLCreateProgram:
7317 {
7318 // If we're capturing, track which programs have been created
7319 gl::ShaderProgramID programID = {call.params.getReturnValue().value.GLuintVal};
7320 handleGennedResource(context, programID);
7321
7322 mResourceTracker.setShaderProgramType(programID, ShaderProgramType::ProgramType);
7323 break;
7324 }
7325
7326 case EntryPoint::GLDeleteProgram:
7327 {
7328 // If we're capturing, track which programs have been deleted
7329 const ParamCapture ¶m =
7330 call.params.getParam("programPacked", ParamType::TShaderProgramID, 0);
7331 handleDeletedResource(context, param.value.ShaderProgramIDVal);
7332
7333 // If this assert fires, it means a ShaderProgramID has changed from program to shader
7334 // which is unsupported
7335 ASSERT(mResourceTracker.getShaderProgramType(param.value.ShaderProgramIDVal) ==
7336 ShaderProgramType::ProgramType);
7337
7338 break;
7339 }
7340
7341 case EntryPoint::GLCreateShader:
7342 {
7343 // If we're capturing, track which shaders have been created
7344 gl::ShaderProgramID shaderID = {call.params.getReturnValue().value.GLuintVal};
7345 handleGennedResource(context, shaderID);
7346
7347 mResourceTracker.setShaderProgramType(shaderID, ShaderProgramType::ShaderType);
7348 break;
7349 }
7350
7351 case EntryPoint::GLDeleteShader:
7352 {
7353 // If we're capturing, track which shaders have been deleted
7354 const ParamCapture ¶m =
7355 call.params.getParam("shaderPacked", ParamType::TShaderProgramID, 0);
7356 handleDeletedResource(context, param.value.ShaderProgramIDVal);
7357
7358 // If this assert fires, it means a ShaderProgramID has changed from shader to program
7359 // which is unsupported
7360 ASSERT(mResourceTracker.getShaderProgramType(param.value.ShaderProgramIDVal) ==
7361 ShaderProgramType::ShaderType);
7362 break;
7363 }
7364
7365 case EntryPoint::GLCompileShader:
7366 {
7367 // Refresh the cached shader sources.
7368 gl::ShaderProgramID shaderID =
7369 call.params.getParam("shaderPacked", ParamType::TShaderProgramID, 0)
7370 .value.ShaderProgramIDVal;
7371 const gl::Shader *shader = context->getShader(shaderID);
7372 // Shaders compiled for ProgramBinary will not have a shader created
7373 if (shader)
7374 {
7375 setShaderSource(shaderID, shader->getSourceString());
7376 }
7377 break;
7378 }
7379
7380 case EntryPoint::GLLinkProgram:
7381 {
7382 // Refresh the cached program sources.
7383 gl::ShaderProgramID programID =
7384 call.params.getParam("programPacked", ParamType::TShaderProgramID, 0)
7385 .value.ShaderProgramIDVal;
7386 const gl::Program *program = context->getProgramResolveLink(programID);
7387 // Programs linked in support of ProgramBinary will not have attached shaders
7388 if (program->getState().hasAttachedShader())
7389 {
7390 setProgramSources(programID, GetAttachedProgramSources(program));
7391 }
7392 break;
7393 }
7394
7395 case EntryPoint::GLCompressedTexImage1D:
7396 case EntryPoint::GLCompressedTexSubImage1D:
7397 {
7398 UNIMPLEMENTED();
7399 break;
7400 }
7401
7402 case EntryPoint::GLDeleteTextures:
7403 {
7404 // Free any TextureLevelDataMap entries being tracked for this texture
7405 // This is to cover the scenario where a texture has been created, its
7406 // levels cached, then texture deleted and recreated, receiving the same ID
7407
7408 // Look up how many textures are being deleted
7409 GLsizei n = call.params.getParam("n", ParamType::TGLsizei, 0).value.GLsizeiVal;
7410
7411 // Look up the pointer to list of textures
7412 const gl::TextureID *textureIDs =
7413 call.params.getParam("texturesPacked", ParamType::TTextureIDConstPointer, 1)
7414 .value.TextureIDConstPointerVal;
7415
7416 // For each texture listed for deletion
7417 for (int32_t i = 0; i < n; ++i)
7418 {
7419 // If we're capturing, track what textures have been deleted
7420 handleDeletedResource(context, textureIDs[i]);
7421 }
7422 break;
7423 }
7424
7425 case EntryPoint::GLMapBuffer:
7426 case EntryPoint::GLMapBufferOES:
7427 {
7428 gl::BufferBinding target =
7429 call.params.getParam("targetPacked", ParamType::TBufferBinding, 0)
7430 .value.BufferBindingVal;
7431
7432 GLbitfield access =
7433 call.params.getParam("access", ParamType::TGLenum, 1).value.GLenumVal;
7434
7435 gl::Buffer *buffer = context->getState().getTargetBuffer(target);
7436
7437 GLintptr offset = 0;
7438 GLsizeiptr length = static_cast<GLsizeiptr>(buffer->getSize());
7439
7440 bool writable =
7441 access == GL_WRITE_ONLY_OES || access == GL_WRITE_ONLY || access == GL_READ_WRITE;
7442
7443 FrameCaptureShared *frameCaptureShared =
7444 context->getShareGroup()->getFrameCaptureShared();
7445 frameCaptureShared->trackBufferMapping(context, &call, buffer->id(), buffer, offset,
7446 length, writable, false);
7447 break;
7448 }
7449
7450 case EntryPoint::GLUnmapNamedBuffer:
7451 {
7452 UNIMPLEMENTED();
7453 break;
7454 }
7455
7456 case EntryPoint::GLUnmapBuffer:
7457 case EntryPoint::GLUnmapBufferOES:
7458 {
7459 // See if we need to capture the buffer contents
7460 captureMappedBufferSnapshot(context, call);
7461
7462 // Track that the buffer was unmapped, for use during state reset
7463 gl::BufferBinding target =
7464 call.params.getParam("targetPacked", ParamType::TBufferBinding, 0)
7465 .value.BufferBindingVal;
7466 gl::Buffer *buffer = context->getState().getTargetBuffer(target);
7467 mResourceTracker.setBufferUnmapped(context->id(), buffer->id().value);
7468
7469 // Remove from CoherentBufferTracker
7470 mCoherentBufferTracker.removeBuffer(buffer->id());
7471 break;
7472 }
7473
7474 case EntryPoint::GLBufferData:
7475 case EntryPoint::GLBufferSubData:
7476 {
7477 gl::BufferBinding target =
7478 call.params.getParam("targetPacked", ParamType::TBufferBinding, 0)
7479 .value.BufferBindingVal;
7480
7481 gl::Buffer *buffer = context->getState().getTargetBuffer(target);
7482
7483 // Track that this buffer's contents have been modified
7484 mResourceTracker.getTrackedResource(context->id(), ResourceIDType::Buffer)
7485 .setModifiedResource(buffer->id().value);
7486
7487 // BufferData is equivalent to UnmapBuffer, for what we're tracking.
7488 // From the ES 3.1 spec in BufferData section:
7489 // If any portion of the buffer object is mapped in the current context or any
7490 // context current to another thread, it is as though UnmapBuffer (see section
7491 // 6.3.1) is executed in each such context prior to deleting the existing data
7492 // store.
7493 // Track that the buffer was unmapped, for use during state reset
7494 mResourceTracker.setBufferUnmapped(context->id(), buffer->id().value);
7495
7496 break;
7497 }
7498
7499 case EntryPoint::GLCopyBufferSubData:
7500 {
7501 maybeCaptureCoherentBuffers(context);
7502 break;
7503 }
7504 case EntryPoint::GLFinish:
7505 {
7506 // When using shadow memory we might need to synchronize it here.
7507 if (mCoherentBufferTracker.isShadowMemoryEnabled())
7508 {
7509 mCoherentBufferTracker.maybeUpdateShadowMemory();
7510 }
7511 break;
7512 }
7513 case EntryPoint::GLDeleteFramebuffers:
7514 case EntryPoint::GLDeleteFramebuffersOES:
7515 {
7516 // Look up how many framebuffers are being deleted
7517 GLsizei n = call.params.getParam("n", ParamType::TGLsizei, 0).value.GLsizeiVal;
7518
7519 // Look up the pointer to list of framebuffers
7520 const gl::FramebufferID *framebufferIDs =
7521 call.params.getParam("framebuffersPacked", ParamType::TFramebufferIDConstPointer, 1)
7522 .value.FramebufferIDConstPointerVal;
7523
7524 // For each framebuffer listed for deletion
7525 for (int32_t i = 0; i < n; ++i)
7526 {
7527 // If we're capturing, track what framebuffers have been deleted
7528 handleDeletedResource(context, framebufferIDs[i]);
7529 }
7530 break;
7531 }
7532
7533 case EntryPoint::GLUseProgram:
7534 {
7535 if (isCaptureActive())
7536 {
7537 context->getFrameCapture()->getStateResetHelper().setEntryPointDirty(
7538 EntryPoint::GLUseProgram);
7539 }
7540 break;
7541 }
7542
7543 case EntryPoint::GLGenVertexArrays:
7544 case EntryPoint::GLGenVertexArraysOES:
7545 {
7546 GLsizei count = call.params.getParam("n", ParamType::TGLsizei, 0).value.GLsizeiVal;
7547 const gl::VertexArrayID *arrayIDs =
7548 call.params.getParam("arraysPacked", ParamType::TVertexArrayIDPointer, 1)
7549 .value.VertexArrayIDPointerVal;
7550 for (GLsizei i = 0; i < count; i++)
7551 {
7552 handleGennedResource(context, arrayIDs[i]);
7553 }
7554 break;
7555 }
7556
7557 case EntryPoint::GLDeleteVertexArrays:
7558 case EntryPoint::GLDeleteVertexArraysOES:
7559 {
7560 GLsizei count = call.params.getParam("n", ParamType::TGLsizei, 0).value.GLsizeiVal;
7561 const gl::VertexArrayID *arrayIDs =
7562 call.params.getParam("arraysPacked", ParamType::TVertexArrayIDConstPointer, 1)
7563 .value.VertexArrayIDConstPointerVal;
7564 for (GLsizei i = 0; i < count; i++)
7565 {
7566 // If we're capturing, track which vertex arrays have been deleted
7567 handleDeletedResource(context, arrayIDs[i]);
7568 }
7569 break;
7570 }
7571
7572 case EntryPoint::GLBindVertexArray:
7573 case EntryPoint::GLBindVertexArrayOES:
7574 {
7575 if (isCaptureActive())
7576 {
7577 context->getFrameCapture()->getStateResetHelper().setEntryPointDirty(
7578 EntryPoint::GLBindVertexArray);
7579 }
7580 break;
7581 }
7582 case EntryPoint::GLBlendFunc:
7583 {
7584 if (isCaptureActive())
7585 {
7586 context->getFrameCapture()->getStateResetHelper().setEntryPointDirty(
7587 EntryPoint::GLBlendFunc);
7588 }
7589 break;
7590 }
7591 case EntryPoint::GLBlendFuncSeparate:
7592 {
7593 if (isCaptureActive())
7594 {
7595 context->getFrameCapture()->getStateResetHelper().setEntryPointDirty(
7596 EntryPoint::GLBlendFuncSeparate);
7597 }
7598 break;
7599 }
7600 case EntryPoint::GLBlendEquation:
7601 case EntryPoint::GLBlendEquationSeparate:
7602 {
7603 if (isCaptureActive())
7604 {
7605 context->getFrameCapture()->getStateResetHelper().setEntryPointDirty(
7606 EntryPoint::GLBlendEquationSeparate);
7607 }
7608 break;
7609 }
7610 case EntryPoint::GLColorMask:
7611 {
7612 if (isCaptureActive())
7613 {
7614 context->getFrameCapture()->getStateResetHelper().setEntryPointDirty(
7615 EntryPoint::GLColorMask);
7616 }
7617 break;
7618 }
7619 case EntryPoint::GLBlendColor:
7620 {
7621 if (isCaptureActive())
7622 {
7623 context->getFrameCapture()->getStateResetHelper().setEntryPointDirty(
7624 EntryPoint::GLBlendColor);
7625 }
7626 break;
7627 }
7628
7629 case EntryPoint::GLEGLImageTargetTexture2DOES:
7630 {
7631 gl::TextureType target =
7632 call.params.getParam("targetPacked", ParamType::TTextureType, 0)
7633 .value.TextureTypeVal;
7634 egl::ImageID imageID =
7635 call.params.getParam("imagePacked", ParamType::TImageID, 1).value.ImageIDVal;
7636 mResourceTracker.getTextureIDToImageTable().insert(std::pair<GLuint, egl::ImageID>(
7637 context->getState().getTargetTexture(target)->getId(), imageID));
7638 break;
7639 }
7640
7641 case EntryPoint::EGLCreateImage:
7642 {
7643 CreateEGLImagePreCallUpdate<EGLAttrib>(call, mResourceTracker,
7644 ParamType::TEGLAttribPointer,
7645 egl::AttributeMap::CreateFromAttribArray);
7646 break;
7647 }
7648 case EntryPoint::EGLCreateImageKHR:
7649 {
7650 CreateEGLImagePreCallUpdate<EGLint>(call, mResourceTracker, ParamType::TEGLintPointer,
7651 egl::AttributeMap::CreateFromIntArray);
7652 break;
7653 }
7654 case EntryPoint::EGLCreateSync:
7655 case EntryPoint::EGLCreateSyncKHR:
7656 {
7657 egl::SyncID eglSyncID = call.params.getReturnValue().value.egl_SyncIDVal;
7658 FrameCaptureShared *frameCaptureShared =
7659 context->getShareGroup()->getFrameCaptureShared();
7660 // If we're capturing, track which egl sync has been created
7661 if (frameCaptureShared->isCaptureActive())
7662 {
7663 handleGennedResource(context, eglSyncID);
7664 }
7665 break;
7666 }
7667 case EntryPoint::EGLDestroySync:
7668 case EntryPoint::EGLDestroySyncKHR:
7669 {
7670 egl::SyncID eglSyncID =
7671 call.params.getParam("syncPacked", ParamType::Tegl_SyncID, 1).value.egl_SyncIDVal;
7672 FrameCaptureShared *frameCaptureShared =
7673 context->getShareGroup()->getFrameCaptureShared();
7674 // If we're capturing, track which EGL sync has been deleted
7675 if (frameCaptureShared->isCaptureActive())
7676 {
7677 handleDeletedResource(context, eglSyncID);
7678 }
7679 break;
7680 }
7681 case EntryPoint::GLDispatchCompute:
7682 {
7683 // When using shadow memory we need to update the real memory here
7684 if (mCoherentBufferTracker.isShadowMemoryEnabled())
7685 {
7686 maybeCaptureCoherentBuffers(context);
7687 }
7688 break;
7689 }
7690 default:
7691 break;
7692 }
7693
7694 if (IsTextureUpdate(call))
7695 {
7696 // If this call modified texture contents, track it for possible reset
7697 trackTextureUpdate(context, call);
7698 }
7699
7700 if (isCaptureActive() && GetDefaultUniformType(call) != DefaultUniformType::None)
7701 {
7702 trackDefaultUniformUpdate(context, call);
7703 }
7704
7705 if (IsVertexArrayUpdate(call))
7706 {
7707 trackVertexArrayUpdate(context, call);
7708 }
7709
7710 updateReadBufferSize(call.params.getReadBufferSize());
7711
7712 std::vector<gl::ShaderProgramID> shaderProgramIDs;
7713 if (FindShaderProgramIDsInCall(call, shaderProgramIDs))
7714 {
7715 for (gl::ShaderProgramID shaderProgramID : shaderProgramIDs)
7716 {
7717 mResourceTracker.onShaderProgramAccess(shaderProgramID);
7718
7719 if (isCaptureActive())
7720 {
7721 // Track that this call referenced a ShaderProgram, setting it active for Setup
7722 MarkResourceIDActive(ResourceIDType::ShaderProgram, shaderProgramID.value,
7723 shareGroupSetupCalls, resourceIDToSetupCalls);
7724 }
7725 }
7726 }
7727 }
7728
7729 template <typename ParamValueType>
maybeGenResourceOnBind(const gl::Context * context,CallCapture & call)7730 void FrameCaptureShared::maybeGenResourceOnBind(const gl::Context *context, CallCapture &call)
7731 {
7732 const char *paramName = ParamValueTrait<ParamValueType>::name;
7733 const ParamType paramType = ParamValueTrait<ParamValueType>::typeID;
7734
7735 const ParamCapture ¶m = call.params.getParam(paramName, paramType, 1);
7736 const ParamValueType id = AccessParamValue<ParamValueType>(paramType, param.value);
7737
7738 // Don't inject the default resource or resources that are already generated
7739 if (id.value != 0 && !resourceIsGenerated(context, id))
7740 {
7741 handleGennedResource(context, id);
7742
7743 ResourceIDType resourceIDType = GetResourceIDTypeFromParamType(param.type);
7744 const char *resourceName = GetResourceIDTypeName(resourceIDType);
7745
7746 std::stringstream updateFuncNameStr;
7747 updateFuncNameStr << "Set" << resourceName << "ID";
7748 std::string updateFuncName = updateFuncNameStr.str();
7749
7750 ParamBuffer params;
7751 params.addValueParam("id", ParamType::TGLuint, id.value);
7752 mFrameCalls.emplace_back(updateFuncName, std::move(params));
7753 }
7754 }
7755
updateResourceCountsFromParamCapture(const ParamCapture & param,ResourceIDType idType)7756 void FrameCaptureShared::updateResourceCountsFromParamCapture(const ParamCapture ¶m,
7757 ResourceIDType idType)
7758 {
7759 if (idType != ResourceIDType::InvalidEnum)
7760 {
7761 mHasResourceType.set(idType);
7762
7763 // Capture resource IDs for non-pointer types.
7764 if (strcmp(ParamTypeToString(param.type), "GLuint") == 0)
7765 {
7766 mMaxAccessedResourceIDs[idType] =
7767 std::max(mMaxAccessedResourceIDs[idType], param.value.GLuintVal);
7768 }
7769 // Capture resource IDs for pointer types.
7770 if (strstr(ParamTypeToString(param.type), "GLuint *") != nullptr)
7771 {
7772 if (param.data.size() == 1u)
7773 {
7774 const GLuint *dataPtr = reinterpret_cast<const GLuint *>(param.data[0].data());
7775 size_t numHandles = param.data[0].size() / sizeof(GLuint);
7776 for (size_t handleIndex = 0; handleIndex < numHandles; ++handleIndex)
7777 {
7778 mMaxAccessedResourceIDs[idType] =
7779 std::max(mMaxAccessedResourceIDs[idType], dataPtr[handleIndex]);
7780 }
7781 }
7782 }
7783 if (idType == ResourceIDType::Sync)
7784 {
7785 mMaxAccessedResourceIDs[idType] =
7786 std::max(mMaxAccessedResourceIDs[idType], param.value.GLuintVal);
7787 }
7788 }
7789 }
7790
updateResourceCountsFromCallCapture(const CallCapture & call)7791 void FrameCaptureShared::updateResourceCountsFromCallCapture(const CallCapture &call)
7792 {
7793 for (const ParamCapture ¶m : call.params.getParamCaptures())
7794 {
7795 ResourceIDType idType = GetResourceIDTypeFromParamType(param.type);
7796 updateResourceCountsFromParamCapture(param, idType);
7797 }
7798
7799 // Update resource IDs in the return value. Return values types are not stored as resource IDs,
7800 // but instead are stored as GLuints. Therefore we need to explicitly label the resource ID type
7801 // when we call update. Currently only shader and program creation are explicitly tracked.
7802 switch (call.entryPoint)
7803 {
7804 case EntryPoint::GLCreateShader:
7805 case EntryPoint::GLCreateProgram:
7806 updateResourceCountsFromParamCapture(call.params.getReturnValue(),
7807 ResourceIDType::ShaderProgram);
7808 break;
7809
7810 case EntryPoint::GLFenceSync:
7811 updateResourceCountsFromParamCapture(call.params.getReturnValue(),
7812 ResourceIDType::Sync);
7813 break;
7814 case EntryPoint::EGLCreateSync:
7815 case EntryPoint::EGLCreateSyncKHR:
7816 updateResourceCountsFromParamCapture(call.params.getReturnValue(),
7817 ResourceIDType::egl_Sync);
7818 break;
7819 default:
7820 break;
7821 }
7822 }
7823
captureCall(gl::Context * context,CallCapture && inCall,bool isCallValid)7824 void FrameCaptureShared::captureCall(gl::Context *context, CallCapture &&inCall, bool isCallValid)
7825 {
7826 if (SkipCall(inCall.entryPoint))
7827 {
7828 return;
7829 }
7830
7831 if (isCallValid)
7832 {
7833 // If the context ID has changed, then we need to inject an eglMakeCurrent() call. Only do
7834 // this if there is more than 1 context in the share group to avoid unnecessary
7835 // eglMakeCurrent() calls.
7836 size_t contextCount = context->getShareGroup()->getContexts().size();
7837 if (contextCount > 1 && mLastContextId != context->id())
7838 {
7839 // Inject the eglMakeCurrent() call. Ignore the display and surface.
7840 CallCapture makeCurrentCall =
7841 egl::CaptureMakeCurrent(nullptr, true, nullptr, {0}, {0}, context->id(), EGL_TRUE);
7842 mFrameCalls.emplace_back(std::move(makeCurrentCall));
7843 mLastContextId = context->id();
7844 }
7845
7846 // Update resource counts before we override entry points with custom calls.
7847 updateResourceCountsFromCallCapture(inCall);
7848
7849 std::vector<CallCapture> outCalls;
7850 maybeOverrideEntryPoint(context, inCall, outCalls);
7851
7852 // Need to loop on any new calls we added during override
7853 for (CallCapture &call : outCalls)
7854 {
7855 // During capture, consider all frame calls active
7856 if (isCaptureActive())
7857 {
7858 call.isActive = true;
7859 }
7860
7861 maybeCapturePreCallUpdates(context, call, &mShareGroupSetupCalls,
7862 &mResourceIDToSetupCalls);
7863 mFrameCalls.emplace_back(std::move(call));
7864 maybeCapturePostCallUpdates(context);
7865 }
7866
7867 // Evaluate the validation expression to determine if we insert a validation checkpoint.
7868 // This lets the user pick a subset of calls to check instead of checking every call.
7869 if (mValidateSerializedState && !mValidationExpression.empty())
7870 {
7871 // Example substitution for frame #2, call #110:
7872 // Before: (call == 2) && (frame >= 100) && (frame <= 120) && ((frame % 10) == 0)
7873 // After: (2 == 2) && (110 >= 100) && (110 <= 120) && ((110 % 10) == 0)
7874 // Evaluates to 1.0.
7875 std::string expression = mValidationExpression;
7876
7877 angle::ReplaceAllSubstrings(&expression, "frame", std::to_string(mFrameIndex));
7878 angle::ReplaceAllSubstrings(&expression, "call", std::to_string(mFrameCalls.size()));
7879
7880 double result = ceval_result(expression);
7881 if (result > 0)
7882 {
7883 CaptureValidateSerializedState(context, &mFrameCalls);
7884 }
7885 }
7886 }
7887 else
7888 {
7889 const int maxInvalidCallLogs = 3;
7890 size_t &callCount = isCaptureActive() ? mInvalidCallCountsActive[inCall.entryPoint]
7891 : mInvalidCallCountsInactive[inCall.entryPoint];
7892 callCount++;
7893 if (callCount <= maxInvalidCallLogs)
7894 {
7895 std::ostringstream msg;
7896 msg << "FrameCapture (capture " << (isCaptureActive() ? "active" : "inactive")
7897 << "): Not capturing invalid call to " << GetEntryPointName(inCall.entryPoint);
7898 if (callCount == maxInvalidCallLogs)
7899 {
7900 msg << " (will no longer repeat for this entry point)";
7901 }
7902 INFO() << msg.str();
7903 }
7904 }
7905 }
7906
maybeCapturePostCallUpdates(const gl::Context * context)7907 void FrameCaptureShared::maybeCapturePostCallUpdates(const gl::Context *context)
7908 {
7909 // Process resource ID updates.
7910 if (isCaptureActive())
7911 {
7912 MaybeCaptureUpdateResourceIDs(context, &mResourceTracker, &mFrameCalls);
7913 }
7914
7915 CallCapture &lastCall = mFrameCalls.back();
7916 switch (lastCall.entryPoint)
7917 {
7918 case EntryPoint::GLCreateShaderProgramv:
7919 {
7920 gl::ShaderProgramID programId;
7921 programId.value = lastCall.params.getReturnValue().value.GLuintVal;
7922 const gl::Program *program = context->getProgramResolveLink(programId);
7923 CaptureUpdateUniformLocations(program, &mFrameCalls);
7924 CaptureUpdateUniformBlockIndexes(program, &mFrameCalls);
7925 break;
7926 }
7927 case EntryPoint::GLLinkProgram:
7928 {
7929 const ParamCapture ¶m =
7930 lastCall.params.getParam("programPacked", ParamType::TShaderProgramID, 0);
7931 const gl::Program *program =
7932 context->getProgramResolveLink(param.value.ShaderProgramIDVal);
7933 CaptureUpdateUniformLocations(program, &mFrameCalls);
7934 CaptureUpdateUniformBlockIndexes(program, &mFrameCalls);
7935 break;
7936 }
7937 case EntryPoint::GLUseProgram:
7938 CaptureUpdateCurrentProgram(lastCall, 0, &mFrameCalls);
7939 break;
7940 case EntryPoint::GLActiveShaderProgram:
7941 CaptureUpdateCurrentProgram(lastCall, 1, &mFrameCalls);
7942 break;
7943 case EntryPoint::GLDeleteProgram:
7944 {
7945 const ParamCapture ¶m =
7946 lastCall.params.getParam("programPacked", ParamType::TShaderProgramID, 0);
7947 CaptureDeleteUniformLocations(param.value.ShaderProgramIDVal, &mFrameCalls);
7948 break;
7949 }
7950 case EntryPoint::GLShaderSource:
7951 {
7952 lastCall.params.setValueParamAtIndex("count", ParamType::TGLsizei, 1, 1);
7953
7954 ParamCapture ¶mLength =
7955 lastCall.params.getParam("length", ParamType::TGLintConstPointer, 3);
7956 paramLength.data.resize(1);
7957 // Set the length parameter to {-1} to signal that the actual string length
7958 // is to be used. Since we store the parameter blob as an array of four uint8_t
7959 // values, we have to pass the binary equivalent of -1.
7960 paramLength.data[0] = {0xff, 0xff, 0xff, 0xff};
7961 break;
7962 }
7963 case EntryPoint::GLBufferData:
7964 case EntryPoint::GLBufferSubData:
7965 {
7966 // When using shadow memory we need to update it from real memory here
7967 if (mCoherentBufferTracker.isShadowMemoryEnabled())
7968 {
7969 gl::BufferBinding target =
7970 lastCall.params.getParam("targetPacked", ParamType::TBufferBinding, 0)
7971 .value.BufferBindingVal;
7972
7973 gl::Buffer *buffer = context->getState().getTargetBuffer(target);
7974 if (mCoherentBufferTracker.haveBuffer(buffer->id()))
7975 {
7976 std::shared_ptr<CoherentBuffer> cb =
7977 mCoherentBufferTracker.mBuffers[buffer->id().value];
7978 cb->removeProtection(PageSharingType::NoneShared);
7979 cb->updateShadowMemory();
7980 cb->protectAll();
7981 }
7982 }
7983 break;
7984 }
7985
7986 case EntryPoint::GLCopyBufferSubData:
7987 {
7988 // When using shadow memory, we need to mark the buffer shadowDirty bit to true
7989 // so it will be synchronized with real memory on the next glFinish call.
7990 if (mCoherentBufferTracker.isShadowMemoryEnabled())
7991 {
7992 gl::BufferBinding target =
7993 lastCall.params.getParam("writeTargetPacked", ParamType::TBufferBinding, 1)
7994 .value.BufferBindingVal;
7995
7996 gl::Buffer *buffer = context->getState().getTargetBuffer(target);
7997 if (mCoherentBufferTracker.haveBuffer(buffer->id()))
7998 {
7999 std::shared_ptr<CoherentBuffer> cb =
8000 mCoherentBufferTracker.mBuffers[buffer->id().value];
8001 // This needs to be synced on glFinish
8002 cb->markShadowDirty();
8003 }
8004 }
8005 break;
8006 }
8007 case EntryPoint::GLDispatchCompute:
8008 {
8009 // When using shadow memory, we need to mark all buffer's shadowDirty bit to true
8010 // so they will be synchronized with real memory on the next glFinish call.
8011 if (mCoherentBufferTracker.isShadowMemoryEnabled())
8012 {
8013 mCoherentBufferTracker.markAllShadowDirty();
8014 }
8015 break;
8016 }
8017 default:
8018 break;
8019 }
8020 }
8021
captureClientArraySnapshot(const gl::Context * context,size_t vertexCount,size_t instanceCount)8022 void FrameCaptureShared::captureClientArraySnapshot(const gl::Context *context,
8023 size_t vertexCount,
8024 size_t instanceCount)
8025 {
8026 const gl::VertexArray *vao = context->getState().getVertexArray();
8027
8028 // Capture client array data.
8029 for (size_t attribIndex : context->getStateCache().getActiveClientAttribsMask())
8030 {
8031 const gl::VertexAttribute &attrib = vao->getVertexAttribute(attribIndex);
8032 const gl::VertexBinding &binding = vao->getVertexBinding(attrib.bindingIndex);
8033
8034 int callIndex = mClientVertexArrayMap[attribIndex];
8035
8036 if (callIndex != -1)
8037 {
8038 size_t count = vertexCount;
8039
8040 if (binding.getDivisor() > 0)
8041 {
8042 count = rx::UnsignedCeilDivide(static_cast<uint32_t>(instanceCount),
8043 binding.getDivisor());
8044 }
8045
8046 // The last capture element doesn't take up the full stride.
8047 size_t bytesToCapture = (count - 1) * binding.getStride() + attrib.format->pixelBytes;
8048
8049 CallCapture &call = mFrameCalls[callIndex];
8050 ParamCapture ¶m = call.params.getClientArrayPointerParameter();
8051 ASSERT(param.type == ParamType::TvoidConstPointer);
8052
8053 ParamBuffer updateParamBuffer;
8054 updateParamBuffer.addValueParam<GLint>("arrayIndex", ParamType::TGLint,
8055 static_cast<uint32_t>(attribIndex));
8056
8057 ParamCapture updateMemory("pointer", ParamType::TvoidConstPointer);
8058 CaptureMemory(param.value.voidConstPointerVal, bytesToCapture, &updateMemory);
8059 updateParamBuffer.addParam(std::move(updateMemory));
8060
8061 updateParamBuffer.addValueParam<GLuint64>("size", ParamType::TGLuint64, bytesToCapture);
8062
8063 mFrameCalls.emplace_back("UpdateClientArrayPointer", std::move(updateParamBuffer));
8064
8065 mClientArraySizes[attribIndex] =
8066 std::max(mClientArraySizes[attribIndex], bytesToCapture);
8067 }
8068 }
8069 }
8070
captureCoherentBufferSnapshot(const gl::Context * context,gl::BufferID id)8071 void FrameCaptureShared::captureCoherentBufferSnapshot(const gl::Context *context, gl::BufferID id)
8072 {
8073 if (!hasBufferData(id))
8074 {
8075 // This buffer was not marked writable
8076 return;
8077 }
8078
8079 const gl::State &apiState = context->getState();
8080 const gl::BufferManager &buffers = apiState.getBufferManagerForCapture();
8081 gl::Buffer *buffer = buffers.getBuffer(id);
8082 if (!buffer)
8083 {
8084 // Could not find buffer binding
8085 return;
8086 }
8087
8088 ASSERT(buffer->isMapped());
8089
8090 std::shared_ptr<angle::CoherentBuffer> coherentBuffer =
8091 mCoherentBufferTracker.mBuffers[id.value];
8092
8093 std::vector<PageRange> dirtyPageRanges = coherentBuffer->getDirtyPageRanges();
8094
8095 if (mCoherentBufferTracker.isShadowMemoryEnabled() && !dirtyPageRanges.empty())
8096 {
8097 coherentBuffer->updateBufferMemory();
8098 }
8099
8100 AddressRange wholeRange = coherentBuffer->getRange();
8101
8102 for (PageRange &pageRange : dirtyPageRanges)
8103 {
8104 // Write protect the memory already, so the app is blocked on writing during our capture
8105 coherentBuffer->protectPageRange(pageRange);
8106
8107 // Create the parameters to our helper for use during replay
8108 ParamBuffer dataParamBuffer;
8109
8110 // Pass in the target buffer ID
8111 dataParamBuffer.addValueParam("dest", ParamType::TGLuint, buffer->id().value);
8112
8113 // Capture the current buffer data with a binary param
8114 ParamCapture captureData("source", ParamType::TvoidConstPointer);
8115
8116 AddressRange dirtyRange = coherentBuffer->getDirtyAddressRange(pageRange);
8117 CaptureMemory(reinterpret_cast<void *>(dirtyRange.start), dirtyRange.size, &captureData);
8118 dataParamBuffer.addParam(std::move(captureData));
8119
8120 // Also track its size for use with memcpy
8121 dataParamBuffer.addValueParam<GLsizeiptr>("size", ParamType::TGLsizeiptr,
8122 static_cast<GLsizeiptr>(dirtyRange.size));
8123
8124 if (wholeRange.start != dirtyRange.start)
8125 {
8126 // Capture with offset
8127 GLsizeiptr offset = dirtyRange.start - wholeRange.start;
8128
8129 ASSERT(offset > 0);
8130
8131 // The dirty page range is not at the start of the buffer, track the offset.
8132 dataParamBuffer.addValueParam<GLsizeiptr>("offset", ParamType::TGLsizeiptr, offset);
8133
8134 // Call the helper that populates the buffer with captured data
8135 mFrameCalls.emplace_back("UpdateClientBufferDataWithOffset",
8136 std::move(dataParamBuffer));
8137 }
8138 else
8139 {
8140 // Call the helper that populates the buffer with captured data
8141 mFrameCalls.emplace_back("UpdateClientBufferData", std::move(dataParamBuffer));
8142 }
8143 }
8144 }
8145
captureMappedBufferSnapshot(const gl::Context * context,const CallCapture & call)8146 void FrameCaptureShared::captureMappedBufferSnapshot(const gl::Context *context,
8147 const CallCapture &call)
8148 {
8149 // If the buffer was mapped writable, we need to restore its data, since we have no
8150 // visibility into what the client did to the buffer while mapped.
8151 // This sequence will result in replay calls like this:
8152 // ...
8153 // gMappedBufferData[gBufferMap[42]] = glMapBufferRange(GL_PIXEL_UNPACK_BUFFER, 0, 65536,
8154 // GL_MAP_WRITE_BIT);
8155 // ...
8156 // UpdateClientBufferData(42, &gBinaryData[164631024], 65536);
8157 // glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
8158 // ...
8159
8160 // Re-map the buffer, using the info we tracked about the buffer
8161 gl::BufferBinding target =
8162 call.params.getParam("targetPacked", ParamType::TBufferBinding, 0).value.BufferBindingVal;
8163
8164 FrameCaptureShared *frameCaptureShared = context->getShareGroup()->getFrameCaptureShared();
8165 gl::Buffer *buffer = context->getState().getTargetBuffer(target);
8166 if (!frameCaptureShared->hasBufferData(buffer->id()))
8167 {
8168 // This buffer was not marked writable, so we did not back it up
8169 return;
8170 }
8171
8172 std::pair<GLintptr, GLsizeiptr> bufferDataOffsetAndLength =
8173 frameCaptureShared->getBufferDataOffsetAndLength(buffer->id());
8174 GLintptr offset = bufferDataOffsetAndLength.first;
8175 GLsizeiptr length = bufferDataOffsetAndLength.second;
8176
8177 // Map the buffer so we can copy its contents out
8178 ASSERT(!buffer->isMapped());
8179 angle::Result result = buffer->mapRange(context, offset, length, GL_MAP_READ_BIT);
8180 if (result != angle::Result::Continue)
8181 {
8182 ERR() << "Failed to mapRange of buffer" << std::endl;
8183 }
8184 const uint8_t *data = reinterpret_cast<const uint8_t *>(buffer->getMapPointer());
8185
8186 // Create the parameters to our helper for use during replay
8187 ParamBuffer dataParamBuffer;
8188
8189 // Pass in the target buffer ID
8190 dataParamBuffer.addValueParam("dest", ParamType::TGLuint, buffer->id().value);
8191
8192 // Capture the current buffer data with a binary param
8193 ParamCapture captureData("source", ParamType::TvoidConstPointer);
8194 CaptureMemory(data, length, &captureData);
8195 dataParamBuffer.addParam(std::move(captureData));
8196
8197 // Also track its size for use with memcpy
8198 dataParamBuffer.addValueParam<GLsizeiptr>("size", ParamType::TGLsizeiptr, length);
8199
8200 // Call the helper that populates the buffer with captured data
8201 mFrameCalls.emplace_back("UpdateClientBufferData", std::move(dataParamBuffer));
8202
8203 // Unmap the buffer and move on
8204 GLboolean dontCare;
8205 (void)buffer->unmap(context, &dontCare);
8206 }
8207
checkForCaptureTrigger()8208 void FrameCaptureShared::checkForCaptureTrigger()
8209 {
8210 // If the capture trigger has not been set, move on
8211 if (mCaptureTrigger == 0)
8212 {
8213 return;
8214 }
8215
8216 // Otherwise, poll the value for a change
8217 std::string captureTriggerStr = GetCaptureTrigger();
8218 if (captureTriggerStr.empty())
8219 {
8220 return;
8221 }
8222
8223 // If the value has changed, use the original value as the frame count
8224 // TODO (anglebug.com/4949): Improve capture at unknown frame time. It is good to
8225 // avoid polling if the feature is not enabled, but not entirely intuitive to set
8226 // a value to zero when you want to trigger it.
8227 uint32_t captureTrigger = atoi(captureTriggerStr.c_str());
8228 if (captureTrigger != mCaptureTrigger)
8229 {
8230 // Start mid-execution capture for the current frame
8231 mCaptureStartFrame = mFrameIndex + 1;
8232
8233 // Use the original trigger value as the frame count
8234 mCaptureEndFrame = mCaptureStartFrame + mCaptureTrigger - 1;
8235
8236 INFO() << "Capture triggered after frame " << mFrameIndex << " for " << mCaptureTrigger
8237 << " frames";
8238
8239 // Stop polling
8240 mCaptureTrigger = 0;
8241 }
8242 }
8243
scanSetupCalls(std::vector<CallCapture> & setupCalls)8244 void FrameCaptureShared::scanSetupCalls(std::vector<CallCapture> &setupCalls)
8245 {
8246 // Scan all the instructions in the list for tracking
8247 for (CallCapture &call : setupCalls)
8248 {
8249 updateReadBufferSize(call.params.getReadBufferSize());
8250 updateResourceCountsFromCallCapture(call);
8251 }
8252 }
8253
runMidExecutionCapture(gl::Context * mainContext)8254 void FrameCaptureShared::runMidExecutionCapture(gl::Context *mainContext)
8255 {
8256 // Set the capture active to ensure all GLES commands issued by the next frame are
8257 // handled correctly by maybeCapturePreCallUpdates() and maybeCapturePostCallUpdates().
8258 setCaptureActive();
8259
8260 // Make sure all pending work for every Context in the share group has completed so all data
8261 // (buffers, textures, etc.) has been updated and no resources are in use.
8262 egl::ShareGroup *shareGroup = mainContext->getShareGroup();
8263 shareGroup->finishAllContexts();
8264
8265 const gl::State &contextState = mainContext->getState();
8266 gl::State mainContextReplayState(
8267 nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, contextState.getClientType(),
8268 contextState.getClientVersion(), contextState.getProfileMask(), false, true, true, true,
8269 false, EGL_CONTEXT_PRIORITY_MEDIUM_IMG, contextState.hasRobustAccess(),
8270 contextState.hasProtectedContent());
8271 mainContextReplayState.initializeForCapture(mainContext);
8272
8273 CaptureShareGroupMidExecutionSetup(mainContext, &mShareGroupSetupCalls, &mResourceTracker,
8274 mainContextReplayState, mMaxAccessedResourceIDs);
8275
8276 scanSetupCalls(mShareGroupSetupCalls);
8277
8278 egl::Display *display = mainContext->getDisplay();
8279 egl::Surface *draw = mainContext->getCurrentDrawSurface();
8280 egl::Surface *read = mainContext->getCurrentReadSurface();
8281
8282 for (auto shareContext : shareGroup->getContexts())
8283 {
8284 FrameCapture *frameCapture = shareContext.second->getFrameCapture();
8285 ASSERT(frameCapture->getSetupCalls().empty());
8286
8287 if (shareContext.second->id() == mainContext->id())
8288 {
8289 CaptureMidExecutionSetup(shareContext.second, &frameCapture->getSetupCalls(),
8290 frameCapture->getStateResetHelper().getResetCalls(),
8291 &mShareGroupSetupCalls, &mResourceIDToSetupCalls,
8292 &mResourceTracker, mainContextReplayState,
8293 mValidateSerializedState);
8294 scanSetupCalls(frameCapture->getSetupCalls());
8295
8296 std::stringstream protoStream;
8297 std::stringstream headerStream;
8298 std::stringstream bodyStream;
8299
8300 protoStream << "void "
8301 << FmtSetupFunction(kNoPartId, mainContext->id(), FuncUsage::Prototype);
8302 std::string proto = protoStream.str();
8303
8304 WriteCppReplayFunctionWithParts(mainContext->id(), ReplayFunc::Setup, mReplayWriter, 1,
8305 &mBinaryData, frameCapture->getSetupCalls(),
8306 headerStream, bodyStream, &mResourceIDBufferSize);
8307
8308 mReplayWriter.addPrivateFunction(proto, headerStream, bodyStream);
8309 }
8310 else
8311 {
8312 const gl::State &shareContextState = shareContext.second->getState();
8313 gl::State auxContextReplayState(
8314 nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
8315 shareContextState.getClientType(), shareContextState.getClientVersion(),
8316 shareContextState.getProfileMask(), false, true, true, true, false,
8317 EGL_CONTEXT_PRIORITY_MEDIUM_IMG, shareContextState.hasRobustAccess(),
8318 shareContextState.hasProtectedContent());
8319 auxContextReplayState.initializeForCapture(shareContext.second);
8320
8321 egl::Error error = shareContext.second->makeCurrent(display, draw, read);
8322 if (error.isError())
8323 {
8324 INFO() << "MEC unable to make secondary context current";
8325 }
8326
8327 CaptureMidExecutionSetup(shareContext.second, &frameCapture->getSetupCalls(),
8328 frameCapture->getStateResetHelper().getResetCalls(),
8329 &mShareGroupSetupCalls, &mResourceIDToSetupCalls,
8330 &mResourceTracker, auxContextReplayState,
8331 mValidateSerializedState);
8332
8333 scanSetupCalls(frameCapture->getSetupCalls());
8334
8335 WriteAuxiliaryContextCppSetupReplay(
8336 mReplayWriter, mCompression, mOutDirectory, shareContext.second, mCaptureLabel, 1,
8337 frameCapture->getSetupCalls(), &mBinaryData, mSerializeStateEnabled, *this,
8338 &mResourceIDBufferSize);
8339 }
8340 // Track that this context was created before MEC started
8341 mActiveContexts.insert(shareContext.first);
8342 }
8343
8344 egl::Error error = mainContext->makeCurrent(display, draw, read);
8345 if (error.isError())
8346 {
8347 INFO() << "MEC unable to make main context current again";
8348 }
8349 }
8350
onEndFrame(gl::Context * context)8351 void FrameCaptureShared::onEndFrame(gl::Context *context)
8352 {
8353 if (!enabled() || mFrameIndex > mCaptureEndFrame)
8354 {
8355 setCaptureInactive();
8356 mCoherentBufferTracker.onEndFrame();
8357 return;
8358 }
8359
8360 FrameCapture *frameCapture = context->getFrameCapture();
8361
8362 // Count resource IDs. This is also done on every frame. It could probably be done by
8363 // checking the GL state instead of the calls.
8364 for (const CallCapture &call : mFrameCalls)
8365 {
8366 for (const ParamCapture ¶m : call.params.getParamCaptures())
8367 {
8368 ResourceIDType idType = GetResourceIDTypeFromParamType(param.type);
8369 if (idType != ResourceIDType::InvalidEnum)
8370 {
8371 mHasResourceType.set(idType);
8372 }
8373 }
8374 }
8375
8376 mWindowSurfaceContextID = context->id();
8377
8378 // On Android, we can trigger a capture during the run
8379 checkForCaptureTrigger();
8380
8381 // Check for MEC. Done after checkForCaptureTrigger(), since that can modify mCaptureStartFrame.
8382 if (mFrameIndex < mCaptureStartFrame)
8383 {
8384 if (mFrameIndex == mCaptureStartFrame - 1)
8385 {
8386 // Trigger MEC.
8387 runMidExecutionCapture(context);
8388 }
8389 mFrameIndex++;
8390 reset();
8391 return;
8392 }
8393
8394 ASSERT(isCaptureActive());
8395
8396 if (!mFrameCalls.empty())
8397 {
8398 mActiveFrameIndices.push_back(getReplayFrameIndex());
8399 }
8400
8401 // Make sure all pending work for every Context in the share group has completed so all data
8402 // (buffers, textures, etc.) has been updated and no resources are in use.
8403 egl::ShareGroup *shareGroup = context->getShareGroup();
8404 shareGroup->finishAllContexts();
8405
8406 // Only validate the first frame for now to save on retracing time.
8407 if (mValidateSerializedState && mFrameIndex == mCaptureStartFrame)
8408 {
8409 CaptureValidateSerializedState(context, &mFrameCalls);
8410 }
8411
8412 writeMainContextCppReplay(context, frameCapture->getSetupCalls(),
8413 frameCapture->getStateResetHelper());
8414
8415 if (mFrameIndex == mCaptureEndFrame)
8416 {
8417 // Write shared MEC after frame sequence so we can eliminate unused assets like programs
8418 WriteShareGroupCppSetupReplay(mReplayWriter, mCompression, mOutDirectory, mCaptureLabel, 1,
8419 1, mShareGroupSetupCalls, &mResourceTracker, &mBinaryData,
8420 mSerializeStateEnabled, mWindowSurfaceContextID,
8421 &mResourceIDBufferSize);
8422
8423 // Save the index files after the last frame.
8424 writeCppReplayIndexFiles(context, false);
8425 SaveBinaryData(mCompression, mOutDirectory, kSharedContextId, mCaptureLabel, mBinaryData);
8426 mBinaryData.clear();
8427 mWroteIndexFile = true;
8428 }
8429
8430 reset();
8431 mFrameIndex++;
8432 }
8433
onDestroyContext(const gl::Context * context)8434 void FrameCaptureShared::onDestroyContext(const gl::Context *context)
8435 {
8436 if (!mEnabled)
8437 {
8438 return;
8439 }
8440 if (!mWroteIndexFile && mFrameIndex > mCaptureStartFrame)
8441 {
8442 // If context is destroyed before end frame is reached and at least
8443 // 1 frame has been recorded, then write the index files.
8444 // It doesn't make sense to write the index files when no frame has been recorded
8445 mFrameIndex -= 1;
8446 mCaptureEndFrame = mFrameIndex;
8447 writeCppReplayIndexFiles(context, true);
8448 SaveBinaryData(mCompression, mOutDirectory, kSharedContextId, mCaptureLabel, mBinaryData);
8449 mBinaryData.clear();
8450 mWroteIndexFile = true;
8451 }
8452 }
8453
onMakeCurrent(const gl::Context * context,const egl::Surface * drawSurface)8454 void FrameCaptureShared::onMakeCurrent(const gl::Context *context, const egl::Surface *drawSurface)
8455 {
8456 if (!drawSurface)
8457 {
8458 return;
8459 }
8460
8461 // Track the width, height and color space of the draw surface as provided to makeCurrent
8462 SurfaceParams ¶ms = mDrawSurfaceParams[context->id()];
8463 params.extents = gl::Extents(drawSurface->getWidth(), drawSurface->getHeight(), 1);
8464 params.colorSpace = egl::FromEGLenum<egl::ColorSpace>(drawSurface->getGLColorspace());
8465 }
8466
8467 DataCounters::DataCounters() = default;
8468
8469 DataCounters::~DataCounters() = default;
8470
getAndIncrement(EntryPoint entryPoint,const std::string & paramName)8471 int DataCounters::getAndIncrement(EntryPoint entryPoint, const std::string ¶mName)
8472 {
8473 Counter counterKey = {entryPoint, paramName};
8474 return mData[counterKey]++;
8475 }
8476
8477 DataTracker::DataTracker() = default;
8478
8479 DataTracker::~DataTracker() = default;
8480
8481 StringCounters::StringCounters() = default;
8482
8483 StringCounters::~StringCounters() = default;
8484
getStringCounter(const std::vector<std::string> & strings)8485 int StringCounters::getStringCounter(const std::vector<std::string> &strings)
8486 {
8487 const auto &id = mStringCounterMap.find(strings);
8488 if (id == mStringCounterMap.end())
8489 {
8490 return kStringsNotFound;
8491 }
8492 else
8493 {
8494 return mStringCounterMap[strings];
8495 }
8496 }
8497
setStringCounter(const std::vector<std::string> & strings,int & counter)8498 void StringCounters::setStringCounter(const std::vector<std::string> &strings, int &counter)
8499 {
8500 ASSERT(counter >= 0);
8501 mStringCounterMap[strings] = counter;
8502 }
8503
8504 TrackedResource::TrackedResource() = default;
8505
8506 TrackedResource::~TrackedResource() = default;
8507
8508 ResourceTracker::ResourceTracker() = default;
8509
8510 ResourceTracker::~ResourceTracker() = default;
8511
8512 StateResetHelper::StateResetHelper() = default;
8513 StateResetHelper::~StateResetHelper() = default;
8514
setDefaultResetCalls(const gl::Context * context,angle::EntryPoint entryPoint)8515 void StateResetHelper::setDefaultResetCalls(const gl::Context *context,
8516 angle::EntryPoint entryPoint)
8517 {
8518 static const gl::BlendState kDefaultBlendState;
8519
8520 // Populate default reset calls for entrypoints to support looping to beginning
8521 switch (entryPoint)
8522 {
8523 case angle::EntryPoint::GLUseProgram:
8524 {
8525 if (context->getActiveLinkedProgram() &&
8526 context->getActiveLinkedProgram()->id().value != 0)
8527 {
8528 Capture(&mResetCalls[angle::EntryPoint::GLUseProgram],
8529 gl::CaptureUseProgram(context->getState(), true, {0}));
8530 }
8531 break;
8532 }
8533 case angle::EntryPoint::GLBindVertexArray:
8534 {
8535 if (context->getState().getVertexArray()->id().value != 0)
8536 {
8537 VertexArrayCaptureFuncs vertexArrayFuncs(context->isGLES1());
8538 Capture(&mResetCalls[angle::EntryPoint::GLBindVertexArray],
8539 vertexArrayFuncs.bindVertexArray(context->getState(), true, {0}));
8540 }
8541 break;
8542 }
8543 case angle::EntryPoint::GLBlendFunc:
8544 {
8545 Capture(&mResetCalls[angle::EntryPoint::GLBlendFunc],
8546 CaptureBlendFunc(context->getState(), true, kDefaultBlendState.sourceBlendRGB,
8547 kDefaultBlendState.destBlendRGB));
8548 break;
8549 }
8550 case angle::EntryPoint::GLBlendFuncSeparate:
8551 {
8552 Capture(&mResetCalls[angle::EntryPoint::GLBlendFuncSeparate],
8553 CaptureBlendFuncSeparate(
8554 context->getState(), true, kDefaultBlendState.sourceBlendRGB,
8555 kDefaultBlendState.destBlendRGB, kDefaultBlendState.sourceBlendAlpha,
8556 kDefaultBlendState.destBlendAlpha));
8557 break;
8558 }
8559 case angle::EntryPoint::GLBlendEquation:
8560 {
8561 UNREACHABLE(); // GLBlendEquationSeparate is always used instead
8562 break;
8563 }
8564 case angle::EntryPoint::GLBlendEquationSeparate:
8565 {
8566 Capture(&mResetCalls[angle::EntryPoint::GLBlendEquationSeparate],
8567 CaptureBlendEquationSeparate(context->getState(), true,
8568 kDefaultBlendState.blendEquationRGB,
8569 kDefaultBlendState.blendEquationAlpha));
8570 break;
8571 }
8572 case angle::EntryPoint::GLColorMask:
8573 {
8574 Capture(&mResetCalls[angle::EntryPoint::GLColorMask],
8575 CaptureColorMask(context->getState(), true,
8576 gl::ConvertToGLBoolean(kDefaultBlendState.colorMaskRed),
8577 gl::ConvertToGLBoolean(kDefaultBlendState.colorMaskGreen),
8578 gl::ConvertToGLBoolean(kDefaultBlendState.colorMaskBlue),
8579 gl::ConvertToGLBoolean(kDefaultBlendState.colorMaskAlpha)));
8580 break;
8581 }
8582 case angle::EntryPoint::GLBlendColor:
8583 {
8584 Capture(&mResetCalls[angle::EntryPoint::GLBlendColor],
8585 CaptureBlendColor(context->getState(), true, 0, 0, 0, 0));
8586 break;
8587 }
8588 default:
8589 ERR() << "Unhandled entry point in setDefaultResetCalls: "
8590 << GetEntryPointName(entryPoint);
8591 UNREACHABLE();
8592 break;
8593 }
8594 }
8595
setDeletedFenceSync(gl::SyncID sync)8596 void ResourceTracker::setDeletedFenceSync(gl::SyncID sync)
8597 {
8598 ASSERT(sync.value != 0);
8599 if (mStartingFenceSyncs.find(sync) == mStartingFenceSyncs.end())
8600 {
8601 // This is a fence sync created after MEC was initialized. Ignore it.
8602 return;
8603 }
8604
8605 // In this case, the app is deleting a fence sync we started with, we need to regen on loop.
8606 mFenceSyncsToRegen.insert(sync);
8607 }
8608
setModifiedDefaultUniform(gl::ShaderProgramID programID,gl::UniformLocation location)8609 void ResourceTracker::setModifiedDefaultUniform(gl::ShaderProgramID programID,
8610 gl::UniformLocation location)
8611 {
8612 // Pull up or create the list of uniform locations for this program and mark one dirty
8613 mDefaultUniformsToReset[programID].insert(location);
8614 }
8615
setDefaultUniformBaseLocation(gl::ShaderProgramID programID,gl::UniformLocation location,gl::UniformLocation baseLocation)8616 void ResourceTracker::setDefaultUniformBaseLocation(gl::ShaderProgramID programID,
8617 gl::UniformLocation location,
8618 gl::UniformLocation baseLocation)
8619 {
8620 // Track the base location used to populate arrayed uniforms in Setup
8621 mDefaultUniformBaseLocations[{programID, location}] = baseLocation;
8622 }
8623
getTrackedResource(gl::ContextID contextID,ResourceIDType type)8624 TrackedResource &ResourceTracker::getTrackedResource(gl::ContextID contextID, ResourceIDType type)
8625 {
8626 if (IsSharedObjectResource(type))
8627 {
8628 // No need to index with context if shared
8629 return mTrackedResourcesShared[static_cast<uint32_t>(type)];
8630 }
8631 else
8632 {
8633 // For per-context objects, track the resource per-context
8634 return mTrackedResourcesPerContext[contextID][static_cast<uint32_t>(type)];
8635 }
8636 }
8637
getContextIDs(std::set<gl::ContextID> & idsOut)8638 void ResourceTracker::getContextIDs(std::set<gl::ContextID> &idsOut)
8639 {
8640 for (const auto &trackedResourceIterator : mTrackedResourcesPerContext)
8641 {
8642 gl::ContextID contextID = trackedResourceIterator.first;
8643 idsOut.insert(contextID);
8644 }
8645 }
8646
setGennedResource(GLuint id)8647 void TrackedResource::setGennedResource(GLuint id)
8648 {
8649 if (mStartingResources.find(id) == mStartingResources.end())
8650 {
8651 // This is a resource created after MEC was initialized, track it
8652 mNewResources.insert(id);
8653 }
8654 else
8655 {
8656 // In this case, the app is genning a resource with starting ID after previously deleting it
8657 ASSERT(mResourcesToRegen.find(id) != mResourcesToRegen.end());
8658
8659 // For this, we need to delete it again to recreate it.
8660 mResourcesToDelete.insert(id);
8661 }
8662 }
8663
resourceIsGenerated(GLuint id)8664 bool TrackedResource::resourceIsGenerated(GLuint id)
8665 {
8666 return mStartingResources.find(id) != mStartingResources.end() ||
8667 mNewResources.find(id) != mNewResources.end();
8668 }
8669
setDeletedResource(GLuint id)8670 void TrackedResource::setDeletedResource(GLuint id)
8671 {
8672 if (id == 0)
8673 {
8674 // Ignore ID 0
8675 return;
8676 }
8677
8678 if (mNewResources.find(id) != mNewResources.end())
8679 {
8680 // This is a resource created after MEC was initialized, just clear it, since there will be
8681 // no actions required for it to return to starting state.
8682 mNewResources.erase(id);
8683 return;
8684 }
8685
8686 if (mStartingResources.find(id) != mStartingResources.end())
8687 {
8688 // In this case, the app is deleting a resource we started with, we need to regen on loop
8689
8690 // Mark that we don't need to delete this
8691 mResourcesToDelete.erase(id);
8692
8693 // Generate the resource again
8694 mResourcesToRegen.insert(id);
8695
8696 // Also restore its contents
8697 mResourcesToRestore.insert(id);
8698 }
8699
8700 // If none of the above is true, the app is deleting a resource that was never genned.
8701 }
8702
setModifiedResource(GLuint id)8703 void TrackedResource::setModifiedResource(GLuint id)
8704 {
8705 // If this was a starting resource, we need to track it for restore
8706 if (mStartingResources.find(id) != mStartingResources.end())
8707 {
8708 mResourcesToRestore.insert(id);
8709 }
8710 }
8711
setBufferMapped(gl::ContextID contextID,GLuint id)8712 void ResourceTracker::setBufferMapped(gl::ContextID contextID, GLuint id)
8713 {
8714 // If this was a starting buffer, we may need to restore it to original state during Reset.
8715 // Skip buffers that were deleted after the starting point.
8716 const TrackedResource &trackedBuffers = getTrackedResource(contextID, ResourceIDType::Buffer);
8717 const ResourceSet &startingBuffers = trackedBuffers.getStartingResources();
8718 const ResourceSet &buffersToRegen = trackedBuffers.getResourcesToRegen();
8719 if (startingBuffers.find(id) != startingBuffers.end() &&
8720 buffersToRegen.find(id) == buffersToRegen.end())
8721 {
8722 // Track that its current state is mapped (true)
8723 mStartingBuffersMappedCurrent[id] = true;
8724 }
8725 }
8726
setBufferUnmapped(gl::ContextID contextID,GLuint id)8727 void ResourceTracker::setBufferUnmapped(gl::ContextID contextID, GLuint id)
8728 {
8729 // If this was a starting buffer, we may need to restore it to original state during Reset.
8730 // Skip buffers that were deleted after the starting point.
8731 const TrackedResource &trackedBuffers = getTrackedResource(contextID, ResourceIDType::Buffer);
8732 const ResourceSet &startingBuffers = trackedBuffers.getStartingResources();
8733 const ResourceSet &buffersToRegen = trackedBuffers.getResourcesToRegen();
8734 if (startingBuffers.find(id) != startingBuffers.end() &&
8735 buffersToRegen.find(id) == buffersToRegen.end())
8736 {
8737 // Track that its current state is unmapped (false)
8738 mStartingBuffersMappedCurrent[id] = false;
8739 }
8740 }
8741
getStartingBuffersMappedCurrent(GLuint id) const8742 bool ResourceTracker::getStartingBuffersMappedCurrent(GLuint id) const
8743 {
8744 const auto &foundBool = mStartingBuffersMappedCurrent.find(id);
8745 ASSERT(foundBool != mStartingBuffersMappedCurrent.end());
8746 return foundBool->second;
8747 }
8748
getStartingBuffersMappedInitial(GLuint id) const8749 bool ResourceTracker::getStartingBuffersMappedInitial(GLuint id) const
8750 {
8751 const auto &foundBool = mStartingBuffersMappedInitial.find(id);
8752 ASSERT(foundBool != mStartingBuffersMappedInitial.end());
8753 return foundBool->second;
8754 }
8755
onShaderProgramAccess(gl::ShaderProgramID shaderProgramID)8756 void ResourceTracker::onShaderProgramAccess(gl::ShaderProgramID shaderProgramID)
8757 {
8758 mMaxShaderPrograms = std::max(mMaxShaderPrograms, shaderProgramID.value + 1);
8759 }
8760
isCapturing() const8761 bool FrameCaptureShared::isCapturing() const
8762 {
8763 // Currently we will always do a capture up until the last frame. In the future we could improve
8764 // mid execution capture by only capturing between the start and end frames. The only necessary
8765 // reason we need to capture before the start is for attached program and shader sources.
8766 return mEnabled && mFrameIndex <= mCaptureEndFrame;
8767 }
8768
getFrameCount() const8769 uint32_t FrameCaptureShared::getFrameCount() const
8770 {
8771 return mCaptureEndFrame - mCaptureStartFrame + 1;
8772 }
8773
getReplayFrameIndex() const8774 uint32_t FrameCaptureShared::getReplayFrameIndex() const
8775 {
8776 return mFrameIndex - mCaptureStartFrame + 1;
8777 }
8778
8779 // Serialize trace metadata into a JSON file. The JSON file will be named "trace_prefix.json".
8780 //
8781 // As of writing, it will have the format like so:
8782 // {
8783 // "TraceMetadata":
8784 // {
8785 // "AreClientArraysEnabled" : 1, "CaptureRevision" : 16631, "ConfigAlphaBits" : 8,
8786 // "ConfigBlueBits" : 8, "ConfigDepthBits" : 24, "ConfigGreenBits" : 8,
8787 // ... etc ...
writeJSON(const gl::Context * context)8788 void FrameCaptureShared::writeJSON(const gl::Context *context)
8789 {
8790 const gl::ContextID contextId = context->id();
8791 const SurfaceParams &surfaceParams = mDrawSurfaceParams.at(contextId);
8792 const gl::State &glState = context->getState();
8793 const egl::Config *config = context->getConfig();
8794 const egl::AttributeMap &displayAttribs = context->getDisplay()->getAttributeMap();
8795
8796 unsigned int frameCount = getFrameCount();
8797
8798 JsonSerializer json;
8799 json.startGroup("TraceMetadata");
8800 json.addScalar("CaptureRevision", GetANGLERevision());
8801 json.addScalar("ContextClientMajorVersion", context->getClientMajorVersion());
8802 json.addScalar("ContextClientMinorVersion", context->getClientMinorVersion());
8803 json.addHexValue("DisplayPlatformType", displayAttribs.getAsInt(EGL_PLATFORM_ANGLE_TYPE_ANGLE));
8804 json.addHexValue("DisplayDeviceType",
8805 displayAttribs.getAsInt(EGL_PLATFORM_ANGLE_DEVICE_TYPE_ANGLE));
8806 json.addScalar("FrameStart", 1);
8807 json.addScalar("FrameEnd", frameCount);
8808 json.addScalar("DrawSurfaceWidth", surfaceParams.extents.width);
8809 json.addScalar("DrawSurfaceHeight", surfaceParams.extents.height);
8810 json.addHexValue("DrawSurfaceColorSpace", ToEGLenum(surfaceParams.colorSpace));
8811 if (config)
8812 {
8813 json.addScalar("ConfigRedBits", config->redSize);
8814 json.addScalar("ConfigGreenBits", config->greenSize);
8815 json.addScalar("ConfigBlueBits", config->blueSize);
8816 json.addScalar("ConfigAlphaBits", config->alphaSize);
8817 json.addScalar("ConfigDepthBits", config->depthSize);
8818 json.addScalar("ConfigStencilBits", config->stencilSize);
8819 }
8820 else
8821 {
8822 json.addScalar("ConfigRedBits", EGL_DONT_CARE);
8823 json.addScalar("ConfigGreenBits", EGL_DONT_CARE);
8824 json.addScalar("ConfigBlueBits", EGL_DONT_CARE);
8825 json.addScalar("ConfigAlphaBits", EGL_DONT_CARE);
8826 json.addScalar("ConfigDepthBits", EGL_DONT_CARE);
8827 json.addScalar("ConfigStencilBits", EGL_DONT_CARE);
8828 }
8829 json.addBool("IsBinaryDataCompressed", mCompression);
8830 json.addBool("AreClientArraysEnabled", glState.areClientArraysEnabled());
8831 json.addBool("IsBindGeneratesResourcesEnabled", glState.isBindGeneratesResourceEnabled());
8832 json.addBool("IsWebGLCompatibilityEnabled", glState.isWebGL());
8833 json.addBool("IsRobustResourceInitEnabled", glState.isRobustResourceInitEnabled());
8834 json.addBool("IsTrimmingEnabled", mTrimEnabled);
8835 json.endGroup();
8836
8837 {
8838 const std::vector<std::string> &traceFiles = mReplayWriter.getAndResetWrittenFiles();
8839 json.addVectorOfStrings("TraceFiles", traceFiles);
8840 }
8841
8842 json.addScalar("WindowSurfaceContextID", contextId.value);
8843
8844 {
8845 std::stringstream jsonFileNameStream;
8846 jsonFileNameStream << mOutDirectory << FmtCapturePrefix(kNoContextId, mCaptureLabel)
8847 << ".json";
8848 std::string jsonFileName = jsonFileNameStream.str();
8849
8850 SaveFileHelper saveData(jsonFileName);
8851 saveData.write(reinterpret_cast<const uint8_t *>(json.data()), json.length());
8852 }
8853 }
8854
writeCppReplayIndexFiles(const gl::Context * context,bool writeResetContextCall)8855 void FrameCaptureShared::writeCppReplayIndexFiles(const gl::Context *context,
8856 bool writeResetContextCall)
8857 {
8858 // Ensure the last frame is written. This will no-op if the frame is already written.
8859 mReplayWriter.saveFrame();
8860
8861 const gl::ContextID contextId = context->id();
8862
8863 {
8864 std::stringstream header;
8865
8866 header << "#pragma once\n";
8867 header << "\n";
8868 header << "#include <EGL/egl.h>\n";
8869 header << "#include <stdint.h>\n";
8870
8871 std::string includes = header.str();
8872 mReplayWriter.setHeaderPrologue(includes);
8873 }
8874
8875 {
8876 std::stringstream source;
8877
8878 source << "#include \"" << FmtCapturePrefix(contextId, mCaptureLabel) << ".h\"\n";
8879 source << "#include \"trace_fixture.h\"\n";
8880 source << "#include \"angle_trace_gl.h\"\n";
8881
8882 std::string sourcePrologue = source.str();
8883 mReplayWriter.setSourcePrologue(sourcePrologue);
8884 }
8885
8886 {
8887 std::string proto = "void InitReplay(void)";
8888
8889 std::stringstream source;
8890 source << proto << "\n";
8891 source << "{\n";
8892 WriteInitReplayCall(mCompression, source, context->id(), mCaptureLabel,
8893 MaxClientArraySize(mClientArraySizes), mReadBufferSize,
8894 mResourceIDBufferSize, mMaxAccessedResourceIDs);
8895 source << "}\n";
8896
8897 mReplayWriter.addPrivateFunction(proto, std::stringstream(), source);
8898 }
8899
8900 {
8901 std::string proto = "void ReplayFrame(uint32_t frameIndex)";
8902
8903 std::stringstream source;
8904
8905 source << proto << "\n";
8906 source << "{\n";
8907 source << " switch (frameIndex)\n";
8908 source << " {\n";
8909 for (uint32_t frameIndex : mActiveFrameIndices)
8910 {
8911 source << " case " << frameIndex << ":\n";
8912 source << " " << FmtReplayFunction(contextId, FuncUsage::Call, frameIndex)
8913 << ";\n";
8914 source << " break;\n";
8915 }
8916 source << " default:\n";
8917 source << " break;\n";
8918 source << " }\n";
8919 source << "}\n";
8920
8921 mReplayWriter.addPublicFunction(proto, std::stringstream(), source);
8922 }
8923
8924 if (writeResetContextCall)
8925 {
8926 std::string proto = "void ResetReplay(void)";
8927
8928 std::stringstream source;
8929
8930 source << proto << "\n";
8931 source << "{\n";
8932 source << " // Reset context is empty because context is destroyed before end "
8933 "frame is reached\n";
8934 source << "}\n";
8935
8936 mReplayWriter.addPublicFunction(proto, std::stringstream(), source);
8937 }
8938
8939 if (mSerializeStateEnabled)
8940 {
8941 std::string proto = "const char *GetSerializedContextState(uint32_t frameIndex)";
8942
8943 std::stringstream source;
8944
8945 source << proto << "\n";
8946 source << "{\n";
8947 source << " switch (frameIndex)\n";
8948 source << " {\n";
8949 for (uint32_t frameIndex = 1; frameIndex <= getFrameCount(); ++frameIndex)
8950 {
8951 source << " case " << frameIndex << ":\n";
8952 source << " return "
8953 << FmtGetSerializedContextStateFunction(contextId, FuncUsage::Call, frameIndex)
8954 << ";\n";
8955 }
8956 source << " default:\n";
8957 source << " return NULL;\n";
8958 source << " }\n";
8959 source << "}\n";
8960
8961 mReplayWriter.addPublicFunction(proto, std::stringstream(), source);
8962 }
8963
8964 {
8965 std::stringstream fnameStream;
8966 fnameStream << mOutDirectory << FmtCapturePrefix(contextId, mCaptureLabel);
8967 std::string fnamePattern = fnameStream.str();
8968
8969 mReplayWriter.setFilenamePattern(fnamePattern);
8970 }
8971
8972 mReplayWriter.saveIndexFilesAndHeader();
8973
8974 writeJSON(context);
8975 }
8976
writeMainContextCppReplay(const gl::Context * context,const std::vector<CallCapture> & setupCalls,StateResetHelper & stateResetHelper)8977 void FrameCaptureShared::writeMainContextCppReplay(const gl::Context *context,
8978 const std::vector<CallCapture> &setupCalls,
8979 StateResetHelper &stateResetHelper)
8980 {
8981 ASSERT(mWindowSurfaceContextID == context->id());
8982
8983 {
8984 std::stringstream header;
8985
8986 header << "#include \"" << FmtCapturePrefix(context->id(), mCaptureLabel) << ".h\"\n";
8987 header << "#include \"angle_trace_gl.h\"\n";
8988
8989 std::string headerString = header.str();
8990 mReplayWriter.setSourcePrologue(headerString);
8991 }
8992
8993 uint32_t frameCount = getFrameCount();
8994 uint32_t frameIndex = getReplayFrameIndex();
8995
8996 if (frameIndex == 1)
8997 {
8998 {
8999 std::string proto = "void SetupReplay(void)";
9000
9001 std::stringstream out;
9002
9003 out << proto << "\n";
9004 out << "{\n";
9005
9006 // Setup all of the shared objects.
9007 out << " InitReplay();\n";
9008 if (usesMidExecutionCapture())
9009 {
9010 out << " " << FmtSetupFunction(kNoPartId, kSharedContextId, FuncUsage::Call)
9011 << ";\n";
9012 // Make sure that the current context is mapped correctly
9013 out << " SetCurrentContextID(" << context->id() << ");\n";
9014 }
9015
9016 // Setup each of the auxiliary contexts.
9017 egl::ShareGroup *shareGroup = context->getShareGroup();
9018 const egl::ContextMap &shareContextMap = shareGroup->getContexts();
9019 for (auto shareContext : shareContextMap)
9020 {
9021 if (shareContext.first == context->id().value)
9022 {
9023 if (usesMidExecutionCapture())
9024 {
9025 // Setup the presentation (this) context first.
9026 out << " " << FmtSetupFunction(kNoPartId, context->id(), FuncUsage::Call)
9027 << ";\n";
9028 out << "\n";
9029 }
9030
9031 continue;
9032 }
9033
9034 // The SetupReplayContextXX() calls only exist if this is a mid-execution capture
9035 // and we can only call them if they exist, so only output the calls if this is a
9036 // MEC.
9037 if (usesMidExecutionCapture())
9038 {
9039 // Only call SetupReplayContext for secondary contexts that were current before
9040 // MEC started
9041 if (mActiveContexts.find(shareContext.first) != mActiveContexts.end())
9042 {
9043 // TODO(http://anglebug.com/5878): Support capture/replay of
9044 // eglCreateContext() so this block can be moved into SetupReplayContextXX()
9045 // by injecting them into the beginning of the setup call stream.
9046 out << " CreateContext(" << shareContext.first << ");\n";
9047
9048 out << " "
9049 << FmtSetupFunction(kNoPartId, shareContext.second->id(),
9050 FuncUsage::Call)
9051 << ";\n";
9052 }
9053 }
9054 }
9055
9056 // If there are other contexts that were initialized, we need to make the main context
9057 // current again.
9058 if (shareContextMap.size() > 1)
9059 {
9060 out << "\n";
9061 out << " eglMakeCurrent(NULL, NULL, NULL, gContextMap2[" << context->id()
9062 << "]);\n";
9063 }
9064
9065 out << "}\n";
9066
9067 mReplayWriter.addPublicFunction(proto, std::stringstream(), out);
9068 }
9069 }
9070
9071 // Emit code to reset back to starting state
9072 if (frameIndex == frameCount)
9073 {
9074 std::stringstream resetProtoStream;
9075 std::stringstream resetHeaderStream;
9076 std::stringstream resetBodyStream;
9077
9078 resetProtoStream << "void ResetReplay(void)";
9079
9080 resetBodyStream << resetProtoStream.str() << "\n";
9081 resetBodyStream << "{\n";
9082
9083 // Grab the list of contexts to be reset
9084 std::set<gl::ContextID> contextIDs;
9085 mResourceTracker.getContextIDs(contextIDs);
9086
9087 // TODO(http://anglebug.com/5878): Look at moving this into the shared context file since
9088 // it's resetting shared objects.
9089
9090 // TODO(http://anglebug.com/4599): Support function parts when writing Reset functions
9091
9092 // Track whether anything was written during Reset
9093 bool anyResourceReset = false;
9094
9095 // Track whether we changed contexts during Reset
9096 bool contextChanged = false;
9097
9098 // First emit shared object reset, including opaque and context state
9099 {
9100 std::stringstream protoStream;
9101 std::stringstream headerStream;
9102 std::stringstream bodyStream;
9103
9104 protoStream << "void "
9105 << FmtResetFunction(kNoPartId, kSharedContextId, FuncUsage::Prototype);
9106 bodyStream << protoStream.str() << "\n";
9107 bodyStream << "{\n";
9108
9109 for (ResourceIDType resourceType : AllEnums<ResourceIDType>())
9110 {
9111 if (!IsSharedObjectResource(resourceType))
9112 {
9113 continue;
9114 }
9115 // Use current context for shared reset
9116 MaybeResetResources(context->id(), resourceType, mReplayWriter, bodyStream,
9117 headerStream, &mResourceTracker, &mBinaryData, anyResourceReset,
9118 &mResourceIDBufferSize);
9119 }
9120
9121 // Reset opaque type objects that don't have IDs, so are not ResourceIDTypes.
9122 MaybeResetOpaqueTypeObjects(mReplayWriter, bodyStream, headerStream, context,
9123 &mResourceTracker, &mBinaryData, &mResourceIDBufferSize);
9124
9125 bodyStream << "}\n";
9126
9127 mReplayWriter.addPrivateFunction(protoStream.str(), headerStream, bodyStream);
9128 }
9129
9130 // Emit the call to shared object reset
9131 resetBodyStream << " " << FmtResetFunction(kNoPartId, kSharedContextId, FuncUsage::Call)
9132 << ";\n";
9133
9134 // Reset our output tracker (Note: This was unused during shared reset)
9135 anyResourceReset = false;
9136
9137 // Walk through all contexts that need Reset
9138 for (const gl::ContextID &contextID : contextIDs)
9139 {
9140 // Create a function to reset each context's non-shared objects
9141 {
9142 std::stringstream protoStream;
9143 std::stringstream headerStream;
9144 std::stringstream bodyStream;
9145
9146 protoStream << "void "
9147 << FmtResetFunction(kNoPartId, contextID, FuncUsage::Prototype);
9148 bodyStream << protoStream.str() << "\n";
9149 bodyStream << "{\n";
9150
9151 // Build the Reset calls in a separate stream so we can insert before them
9152 std::stringstream resetStream;
9153
9154 for (ResourceIDType resourceType : AllEnums<ResourceIDType>())
9155 {
9156 if (IsSharedObjectResource(resourceType))
9157 {
9158 continue;
9159 }
9160 MaybeResetResources(contextID, resourceType, mReplayWriter, resetStream,
9161 headerStream, &mResourceTracker, &mBinaryData,
9162 anyResourceReset, &mResourceIDBufferSize);
9163 }
9164
9165 // Only call eglMakeCurrent if anything was actually reset in the function and the
9166 // context differs from current
9167 if (anyResourceReset && contextID != context->id())
9168 {
9169 contextChanged = true;
9170 bodyStream << " eglMakeCurrent(NULL, NULL, NULL, gContextMap2["
9171 << contextID.value << "]);\n\n";
9172 }
9173
9174 // Then append the Reset calls
9175 bodyStream << resetStream.str();
9176
9177 bodyStream << "}\n";
9178 mReplayWriter.addPrivateFunction(protoStream.str(), headerStream, bodyStream);
9179 }
9180
9181 // Emit a call to reset each context's non-shared objects
9182 resetBodyStream << " " << FmtResetFunction(kNoPartId, contextID, FuncUsage::Call)
9183 << ";\n";
9184 }
9185
9186 // Bind the main context again if we bound any additional contexts
9187 if (contextChanged)
9188 {
9189 resetBodyStream << " eglMakeCurrent(NULL, NULL, NULL, gContextMap2["
9190 << context->id().value << "]);\n";
9191 }
9192
9193 // Now that we're back on the main context, reset any additional state
9194 resetBodyStream << "\n // Reset main context state\n";
9195 MaybeResetContextState(mReplayWriter, resetBodyStream, resetHeaderStream, &mResourceTracker,
9196 context, &mBinaryData, stateResetHelper, &mResourceIDBufferSize);
9197
9198 resetBodyStream << "}\n";
9199
9200 mReplayWriter.addPublicFunction(resetProtoStream.str(), resetHeaderStream, resetBodyStream);
9201 }
9202
9203 if (!mFrameCalls.empty())
9204 {
9205 std::stringstream protoStream;
9206 protoStream << "void "
9207 << FmtReplayFunction(context->id(), FuncUsage::Prototype, frameIndex);
9208 std::string proto = protoStream.str();
9209
9210 std::stringstream headerStream;
9211 std::stringstream bodyStream;
9212
9213 WriteCppReplayFunctionWithParts(context->id(), ReplayFunc::Replay, mReplayWriter,
9214 frameIndex, &mBinaryData, mFrameCalls, headerStream,
9215 bodyStream, &mResourceIDBufferSize);
9216
9217 mReplayWriter.addPrivateFunction(proto, headerStream, bodyStream);
9218 }
9219
9220 if (mSerializeStateEnabled)
9221 {
9222 std::string serializedContextString;
9223 if (SerializeContextToString(const_cast<gl::Context *>(context),
9224 &serializedContextString) == Result::Continue)
9225 {
9226 std::stringstream protoStream;
9227 protoStream << "const char *"
9228 << FmtGetSerializedContextStateFunction(context->id(), FuncUsage::Prototype,
9229 frameIndex);
9230 std::string proto = protoStream.str();
9231
9232 std::stringstream bodyStream;
9233 bodyStream << proto << "\n";
9234 bodyStream << "{\n";
9235 bodyStream << " return " << FmtMultiLineString(serializedContextString) << ";\n";
9236 bodyStream << "}\n";
9237
9238 mReplayWriter.addPrivateFunction(proto, std::stringstream(), bodyStream);
9239 }
9240 }
9241
9242 {
9243 std::stringstream fnamePatternStream;
9244 fnamePatternStream << mOutDirectory << FmtCapturePrefix(context->id(), mCaptureLabel);
9245 std::string fnamePattern = fnamePatternStream.str();
9246
9247 mReplayWriter.setFilenamePattern(fnamePattern);
9248 }
9249
9250 if (mFrameIndex == mCaptureEndFrame)
9251 {
9252 mReplayWriter.saveFrame();
9253 }
9254 else
9255 {
9256 mReplayWriter.saveFrameIfFull();
9257 }
9258 }
9259
reset()9260 void FrameCaptureShared::reset()
9261 {
9262 mFrameCalls.clear();
9263 mClientVertexArrayMap.fill(-1);
9264
9265 // Do not reset replay-specific settings like the maximum read buffer size, client array sizes,
9266 // or the 'has seen' type map. We could refine this into per-frame and per-capture maximums if
9267 // necessary.
9268 }
9269
getShaderSource(gl::ShaderProgramID id) const9270 const std::string &FrameCaptureShared::getShaderSource(gl::ShaderProgramID id) const
9271 {
9272 const auto &foundSources = mCachedShaderSource.find(id);
9273 ASSERT(foundSources != mCachedShaderSource.end());
9274 return foundSources->second;
9275 }
9276
setShaderSource(gl::ShaderProgramID id,std::string source)9277 void FrameCaptureShared::setShaderSource(gl::ShaderProgramID id, std::string source)
9278 {
9279 mCachedShaderSource[id] = source;
9280 }
9281
getProgramSources(gl::ShaderProgramID id) const9282 const ProgramSources &FrameCaptureShared::getProgramSources(gl::ShaderProgramID id) const
9283 {
9284 const auto &foundSources = mCachedProgramSources.find(id);
9285 ASSERT(foundSources != mCachedProgramSources.end());
9286 return foundSources->second;
9287 }
9288
setProgramSources(gl::ShaderProgramID id,ProgramSources sources)9289 void FrameCaptureShared::setProgramSources(gl::ShaderProgramID id, ProgramSources sources)
9290 {
9291 mCachedProgramSources[id] = sources;
9292 }
9293
markResourceSetupCallsInactive(std::vector<CallCapture> * setupCalls,ResourceIDType type,GLuint id,gl::Range<size_t> range)9294 void FrameCaptureShared::markResourceSetupCallsInactive(std::vector<CallCapture> *setupCalls,
9295 ResourceIDType type,
9296 GLuint id,
9297 gl::Range<size_t> range)
9298 {
9299 if (!mTrimEnabled)
9300 {
9301 return;
9302 }
9303
9304 ASSERT(mResourceIDToSetupCalls[type].find(id) == mResourceIDToSetupCalls[type].end());
9305
9306 // Mark all of the calls that were used to initialize this resource as INACTIVE
9307 for (size_t index : range)
9308 {
9309 (*setupCalls)[index].isActive = false;
9310 }
9311
9312 mResourceIDToSetupCalls[type][id] = range;
9313 }
9314
CaptureMemory(const void * source,size_t size,ParamCapture * paramCapture)9315 void CaptureMemory(const void *source, size_t size, ParamCapture *paramCapture)
9316 {
9317 std::vector<uint8_t> data(size);
9318 memcpy(data.data(), source, size);
9319 paramCapture->data.emplace_back(std::move(data));
9320 }
9321
CaptureString(const GLchar * str,ParamCapture * paramCapture)9322 void CaptureString(const GLchar *str, ParamCapture *paramCapture)
9323 {
9324 // include the '\0' suffix
9325 CaptureMemory(str, strlen(str) + 1, paramCapture);
9326 }
9327
CaptureStringLimit(const GLchar * str,uint32_t limit,ParamCapture * paramCapture)9328 void CaptureStringLimit(const GLchar *str, uint32_t limit, ParamCapture *paramCapture)
9329 {
9330 // Write the incoming string up to limit, including null terminator
9331 size_t length = strlen(str) + 1;
9332
9333 if (length > limit)
9334 {
9335 // If too many characters, resize the string to fit in the limit
9336 std::string newStr = str;
9337 newStr.resize(limit - 1);
9338 CaptureString(newStr.c_str(), paramCapture);
9339 }
9340 else
9341 {
9342 CaptureMemory(str, length, paramCapture);
9343 }
9344 }
9345
CaptureVertexPointerGLES1(const gl::State & glState,gl::ClientVertexArrayType type,const void * pointer,ParamCapture * paramCapture)9346 void CaptureVertexPointerGLES1(const gl::State &glState,
9347 gl::ClientVertexArrayType type,
9348 const void *pointer,
9349 ParamCapture *paramCapture)
9350 {
9351 paramCapture->value.voidConstPointerVal = pointer;
9352 if (!glState.getTargetBuffer(gl::BufferBinding::Array))
9353 {
9354 paramCapture->arrayClientPointerIndex =
9355 gl::GLES1Renderer::VertexArrayIndex(type, glState.gles1());
9356 }
9357 }
9358
GetProgramForCapture(const gl::State & glState,gl::ShaderProgramID handle)9359 gl::Program *GetProgramForCapture(const gl::State &glState, gl::ShaderProgramID handle)
9360 {
9361 gl::Program *program = glState.getShaderProgramManagerForCapture().getProgram(handle);
9362 return program;
9363 }
9364
CaptureGetActiveUniformBlockivParameters(const gl::State & glState,gl::ShaderProgramID handle,gl::UniformBlockIndex uniformBlockIndex,GLenum pname,ParamCapture * paramCapture)9365 void CaptureGetActiveUniformBlockivParameters(const gl::State &glState,
9366 gl::ShaderProgramID handle,
9367 gl::UniformBlockIndex uniformBlockIndex,
9368 GLenum pname,
9369 ParamCapture *paramCapture)
9370 {
9371 int numParams = 1;
9372
9373 // From the OpenGL ES 3.0 spec:
9374 // If pname is UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES, then a list of the
9375 // active uniform indices for the uniform block identified by uniformBlockIndex is
9376 // returned. The number of elements that will be written to params is the value of
9377 // UNIFORM_BLOCK_ACTIVE_UNIFORMS for uniformBlockIndex
9378 if (pname == GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES)
9379 {
9380 gl::Program *program = GetProgramForCapture(glState, handle);
9381 if (program)
9382 {
9383 gl::QueryActiveUniformBlockiv(program, uniformBlockIndex,
9384 GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS, &numParams);
9385 }
9386 }
9387
9388 paramCapture->readBufferSizeBytes = sizeof(GLint) * numParams;
9389 }
9390
CaptureGetParameter(const gl::State & glState,GLenum pname,size_t typeSize,ParamCapture * paramCapture)9391 void CaptureGetParameter(const gl::State &glState,
9392 GLenum pname,
9393 size_t typeSize,
9394 ParamCapture *paramCapture)
9395 {
9396 // kMaxReportedCapabilities is the biggest array we'll need to hold data from glGet calls.
9397 // This value needs to be updated if any new extensions are introduced that would allow for
9398 // more compressed texture formats. The current value is taken from:
9399 // http://opengles.gpuinfo.org/displaycapability.php?name=GL_NUM_COMPRESSED_TEXTURE_FORMATS&esversion=2
9400 constexpr unsigned int kMaxReportedCapabilities = 69;
9401 paramCapture->readBufferSizeBytes = typeSize * kMaxReportedCapabilities;
9402 }
9403
CaptureGenHandlesImpl(GLsizei n,GLuint * handles,ParamCapture * paramCapture)9404 void CaptureGenHandlesImpl(GLsizei n, GLuint *handles, ParamCapture *paramCapture)
9405 {
9406 paramCapture->readBufferSizeBytes = sizeof(GLuint) * n;
9407 CaptureMemory(handles, paramCapture->readBufferSizeBytes, paramCapture);
9408 }
9409
CaptureShaderStrings(GLsizei count,const GLchar * const * strings,const GLint * length,ParamCapture * paramCapture)9410 void CaptureShaderStrings(GLsizei count,
9411 const GLchar *const *strings,
9412 const GLint *length,
9413 ParamCapture *paramCapture)
9414 {
9415 // Concat the array elements of the string into one data vector,
9416 // append the terminating zero and use this as the captured shader
9417 // string. The string count and the length array are adjusted
9418 // accordingly in the capture post-processing
9419
9420 std::vector<uint8_t> data;
9421 size_t offset = 0;
9422 for (GLsizei index = 0; index < count; ++index)
9423 {
9424 size_t len = ((length && length[index] >= 0) ? length[index] : strlen(strings[index]));
9425
9426 // Count trailing zeros
9427 uint32_t i = 1;
9428 while (i < len && strings[index][len - i] == 0)
9429 {
9430 i++;
9431 }
9432
9433 // Don't copy trailing zeros
9434 len -= (i - 1);
9435
9436 data.resize(offset + len);
9437 std::copy(strings[index], strings[index] + len, data.begin() + offset);
9438 offset += len;
9439 }
9440
9441 data.push_back(0);
9442 paramCapture->data.emplace_back(std::move(data));
9443 }
9444
9445 // ReplayWriter implementation.
ReplayWriter()9446 ReplayWriter::ReplayWriter()
9447 : mSourceFileExtension(kDefaultSourceFileExt),
9448 mSourceFileSizeThreshold(kDefaultSourceFileSizeThreshold),
9449 mFrameIndex(1)
9450 {}
9451
~ReplayWriter()9452 ReplayWriter::~ReplayWriter()
9453 {
9454 ASSERT(mPrivateFunctionPrototypes.empty());
9455 ASSERT(mPublicFunctionPrototypes.empty());
9456 ASSERT(mPrivateFunctions.empty());
9457 ASSERT(mPublicFunctions.empty());
9458 ASSERT(mGlobalVariableDeclarations.empty());
9459 ASSERT(mReplayHeaders.empty());
9460 }
9461
setSourceFileExtension(const char * ext)9462 void ReplayWriter::setSourceFileExtension(const char *ext)
9463 {
9464 mSourceFileExtension = ext;
9465 }
9466
setSourceFileSizeThreshold(size_t sourceFileSizeThreshold)9467 void ReplayWriter::setSourceFileSizeThreshold(size_t sourceFileSizeThreshold)
9468 {
9469 mSourceFileSizeThreshold = sourceFileSizeThreshold;
9470 }
9471
setFilenamePattern(const std::string & pattern)9472 void ReplayWriter::setFilenamePattern(const std::string &pattern)
9473 {
9474 if (mFilenamePattern != pattern)
9475 {
9476 mFilenamePattern = pattern;
9477 }
9478 }
9479
setCaptureLabel(const std::string & label)9480 void ReplayWriter::setCaptureLabel(const std::string &label)
9481 {
9482 mCaptureLabel = label;
9483 }
9484
setSourcePrologue(const std::string & prologue)9485 void ReplayWriter::setSourcePrologue(const std::string &prologue)
9486 {
9487 mSourcePrologue = prologue;
9488 }
9489
setHeaderPrologue(const std::string & prologue)9490 void ReplayWriter::setHeaderPrologue(const std::string &prologue)
9491 {
9492 mHeaderPrologue = prologue;
9493 }
9494
addPublicFunction(const std::string & functionProto,const std::stringstream & headerStream,const std::stringstream & bodyStream)9495 void ReplayWriter::addPublicFunction(const std::string &functionProto,
9496 const std::stringstream &headerStream,
9497 const std::stringstream &bodyStream)
9498 {
9499 mPublicFunctionPrototypes.push_back(functionProto);
9500
9501 std::string header = headerStream.str();
9502 std::string body = bodyStream.str();
9503
9504 if (!header.empty())
9505 {
9506 mReplayHeaders.emplace_back(header);
9507 }
9508
9509 if (!body.empty())
9510 {
9511 mPublicFunctions.emplace_back(body);
9512 }
9513 }
9514
addPrivateFunction(const std::string & functionProto,const std::stringstream & headerStream,const std::stringstream & bodyStream)9515 void ReplayWriter::addPrivateFunction(const std::string &functionProto,
9516 const std::stringstream &headerStream,
9517 const std::stringstream &bodyStream)
9518 {
9519 mPrivateFunctionPrototypes.push_back(functionProto);
9520
9521 std::string header = headerStream.str();
9522 std::string body = bodyStream.str();
9523
9524 if (!header.empty())
9525 {
9526 mReplayHeaders.emplace_back(header);
9527 }
9528
9529 if (!body.empty())
9530 {
9531 mPrivateFunctions.emplace_back(body);
9532 }
9533 }
9534
getInlineVariableName(EntryPoint entryPoint,const std::string & paramName)9535 std::string ReplayWriter::getInlineVariableName(EntryPoint entryPoint, const std::string ¶mName)
9536 {
9537 int counter = mDataTracker.getCounters().getAndIncrement(entryPoint, paramName);
9538 return GetVarName(entryPoint, paramName, counter);
9539 }
9540
getInlineStringSetVariableName(EntryPoint entryPoint,const std::string & paramName,const std::vector<std::string> & strings,bool * isNewEntryOut)9541 std::string ReplayWriter::getInlineStringSetVariableName(EntryPoint entryPoint,
9542 const std::string ¶mName,
9543 const std::vector<std::string> &strings,
9544 bool *isNewEntryOut)
9545 {
9546 int counter = mDataTracker.getStringCounters().getStringCounter(strings);
9547 *isNewEntryOut = (counter == kStringsNotFound);
9548 if (*isNewEntryOut)
9549 {
9550 // This is a unique set of strings, so set up their declaration and update the counter
9551 counter = mDataTracker.getCounters().getAndIncrement(entryPoint, paramName);
9552 mDataTracker.getStringCounters().setStringCounter(strings, counter);
9553
9554 std::string varName = GetVarName(entryPoint, paramName, counter);
9555
9556 std::stringstream declStream;
9557 declStream << "const char *const " << varName << "[]";
9558 std::string decl = declStream.str();
9559
9560 mGlobalVariableDeclarations.push_back(decl);
9561
9562 return varName;
9563 }
9564 else
9565 {
9566 return GetVarName(entryPoint, paramName, counter);
9567 }
9568 }
9569
getStoredReplaySourceSize() const9570 size_t ReplayWriter::getStoredReplaySourceSize() const
9571 {
9572 size_t sum = 0;
9573 for (const std::string &header : mReplayHeaders)
9574 {
9575 sum += header.size();
9576 }
9577 for (const std::string &publicFunc : mPublicFunctions)
9578 {
9579 sum += publicFunc.size();
9580 }
9581 for (const std::string &privateFunc : mPrivateFunctions)
9582 {
9583 sum += privateFunc.size();
9584 }
9585 return sum;
9586 }
9587
9588 // static
GetVarName(EntryPoint entryPoint,const std::string & paramName,int counter)9589 std::string ReplayWriter::GetVarName(EntryPoint entryPoint,
9590 const std::string ¶mName,
9591 int counter)
9592 {
9593 std::stringstream strstr;
9594 strstr << GetEntryPointName(entryPoint) << "_" << paramName << "_" << counter;
9595 return strstr.str();
9596 }
9597
saveFrame()9598 void ReplayWriter::saveFrame()
9599 {
9600 if (mReplayHeaders.empty() && mPublicFunctions.empty() && mPrivateFunctions.empty())
9601 {
9602 return;
9603 }
9604
9605 ASSERT(!mSourceFileExtension.empty());
9606
9607 std::stringstream strstr;
9608 strstr << mFilenamePattern << "_" << std::setfill('0') << std::setw(3) << mFrameIndex++ << "."
9609 << mSourceFileExtension;
9610
9611 std::string frameFilePath = strstr.str();
9612
9613 writeReplaySource(frameFilePath);
9614 }
9615
saveFrameIfFull()9616 void ReplayWriter::saveFrameIfFull()
9617 {
9618 if (getStoredReplaySourceSize() < mSourceFileSizeThreshold)
9619 {
9620 INFO() << "Merging captured frame: " << getStoredReplaySourceSize()
9621 << " less than threshold of " << mSourceFileSizeThreshold << " bytes";
9622 return;
9623 }
9624
9625 saveFrame();
9626 }
9627
saveHeader()9628 void ReplayWriter::saveHeader()
9629 {
9630 std::stringstream headerPathStream;
9631 headerPathStream << mFilenamePattern << ".h";
9632 std::string headerPath = headerPathStream.str();
9633
9634 SaveFileHelper saveH(headerPath);
9635
9636 saveH << mHeaderPrologue << "\n";
9637
9638 saveH << "// Public functions are declared in trace_fixture.h.\n";
9639 saveH << "\n";
9640 saveH << "// Private Functions\n";
9641 saveH << "\n";
9642
9643 for (const std::string &proto : mPrivateFunctionPrototypes)
9644 {
9645 saveH << proto << ";\n";
9646 }
9647
9648 saveH << "\n";
9649 saveH << "// Global variables\n";
9650 saveH << "\n";
9651
9652 for (const std::string &globalVar : mGlobalVariableDeclarations)
9653 {
9654 saveH << "extern " << globalVar << ";\n";
9655 }
9656
9657 mPublicFunctionPrototypes.clear();
9658 mPrivateFunctionPrototypes.clear();
9659 mGlobalVariableDeclarations.clear();
9660
9661 addWrittenFile(headerPath);
9662 }
9663
saveIndexFilesAndHeader()9664 void ReplayWriter::saveIndexFilesAndHeader()
9665 {
9666 ASSERT(!mSourceFileExtension.empty());
9667
9668 std::stringstream sourcePathStream;
9669 sourcePathStream << mFilenamePattern << "." << mSourceFileExtension;
9670 std::string sourcePath = sourcePathStream.str();
9671
9672 writeReplaySource(sourcePath);
9673 saveHeader();
9674 }
9675
saveSetupFile()9676 void ReplayWriter::saveSetupFile()
9677 {
9678 ASSERT(!mSourceFileExtension.empty());
9679
9680 std::stringstream strstr;
9681 strstr << mFilenamePattern << "." << mSourceFileExtension;
9682
9683 std::string frameFilePath = strstr.str();
9684
9685 writeReplaySource(frameFilePath);
9686 }
9687
writeReplaySource(const std::string & filename)9688 void ReplayWriter::writeReplaySource(const std::string &filename)
9689 {
9690 SaveFileHelper saveCpp(filename);
9691
9692 saveCpp << mSourcePrologue << "\n";
9693
9694 for (const std::string &header : mReplayHeaders)
9695 {
9696 saveCpp << header << "\n";
9697 }
9698
9699 saveCpp << "// Private Functions\n";
9700 saveCpp << "\n";
9701
9702 for (const std::string &func : mPrivateFunctions)
9703 {
9704 saveCpp << func << "\n";
9705 }
9706
9707 saveCpp << "// Public Functions\n";
9708 saveCpp << "\n";
9709
9710 if (mFilenamePattern == "cpp")
9711 {
9712 saveCpp << "extern \"C\"\n";
9713 saveCpp << "{\n";
9714 }
9715
9716 for (const std::string &func : mPublicFunctions)
9717 {
9718 saveCpp << func << "\n";
9719 }
9720
9721 if (mFilenamePattern == "cpp")
9722 {
9723 saveCpp << "} // extern \"C\"\n";
9724 }
9725
9726 mReplayHeaders.clear();
9727 mPrivateFunctions.clear();
9728 mPublicFunctions.clear();
9729
9730 addWrittenFile(filename);
9731 }
9732
addWrittenFile(const std::string & filename)9733 void ReplayWriter::addWrittenFile(const std::string &filename)
9734 {
9735 std::string writtenFile = GetBaseName(filename);
9736 ASSERT(std::find(mWrittenFiles.begin(), mWrittenFiles.end(), writtenFile) ==
9737 mWrittenFiles.end());
9738 mWrittenFiles.push_back(writtenFile);
9739 }
9740
getAndResetWrittenFiles()9741 std::vector<std::string> ReplayWriter::getAndResetWrittenFiles()
9742 {
9743 std::vector<std::string> results = std::move(mWrittenFiles);
9744 std::sort(results.begin(), results.end());
9745 ASSERT(mWrittenFiles.empty());
9746 return results;
9747 }
9748 } // namespace angle
9749
9750 namespace egl
9751 {
CaptureAttributeMap(const egl::AttributeMap & attribMap)9752 angle::ParamCapture CaptureAttributeMap(const egl::AttributeMap &attribMap)
9753 {
9754 switch (attribMap.getType())
9755 {
9756 case AttributeMapType::Attrib:
9757 {
9758 angle::ParamCapture paramCapture("attrib_list", angle::ParamType::TEGLAttribPointer);
9759 if (attribMap.isEmpty())
9760 {
9761 paramCapture.value.EGLAttribPointerVal = nullptr;
9762 }
9763 else
9764 {
9765 std::vector<EGLAttrib> attribs;
9766 for (const auto &[key, value] : attribMap)
9767 {
9768 attribs.push_back(key);
9769 attribs.push_back(value);
9770 }
9771 attribs.push_back(EGL_NONE);
9772
9773 angle::CaptureMemory(attribs.data(), attribs.size() * sizeof(EGLAttrib),
9774 ¶mCapture);
9775 }
9776 return paramCapture;
9777 }
9778
9779 case AttributeMapType::Int:
9780 {
9781 angle::ParamCapture paramCapture("attrib_list", angle::ParamType::TEGLintPointer);
9782 if (attribMap.isEmpty())
9783 {
9784 paramCapture.value.EGLintPointerVal = nullptr;
9785 }
9786 else
9787 {
9788 std::vector<EGLint> attribs;
9789 for (const auto &[key, value] : attribMap)
9790 {
9791 attribs.push_back(static_cast<EGLint>(key));
9792 attribs.push_back(static_cast<EGLint>(value));
9793 }
9794 attribs.push_back(EGL_NONE);
9795
9796 angle::CaptureMemory(attribs.data(), attribs.size() * sizeof(EGLint),
9797 ¶mCapture);
9798 }
9799 return paramCapture;
9800 }
9801
9802 default:
9803 UNREACHABLE();
9804 return angle::ParamCapture();
9805 }
9806 }
9807 } // namespace egl
9808