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