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