1 //
2 // Copyright 2019 The ANGLE Project Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 //
6 // FrameCapture.cpp:
7 // ANGLE Frame capture implementation.
8 //
9
10 #include "libANGLE/capture/FrameCapture.h"
11
12 #include <cerrno>
13 #include <cstring>
14 #include <fstream>
15 #include <string>
16
17 #include "sys/stat.h"
18
19 #include "common/angle_version_info.h"
20 #include "common/mathutil.h"
21 #include "common/serializer/JsonSerializer.h"
22 #include "common/string_utils.h"
23 #include "common/system_utils.h"
24 #include "libANGLE/Config.h"
25 #include "libANGLE/Context.h"
26 #include "libANGLE/Display.h"
27 #include "libANGLE/Fence.h"
28 #include "libANGLE/Framebuffer.h"
29 #include "libANGLE/GLES1Renderer.h"
30 #include "libANGLE/Query.h"
31 #include "libANGLE/ResourceMap.h"
32 #include "libANGLE/Shader.h"
33 #include "libANGLE/Surface.h"
34 #include "libANGLE/VertexArray.h"
35 #include "libANGLE/capture/capture_gles_1_0_autogen.h"
36 #include "libANGLE/capture/capture_gles_2_0_autogen.h"
37 #include "libANGLE/capture/capture_gles_3_0_autogen.h"
38 #include "libANGLE/capture/capture_gles_3_1_autogen.h"
39 #include "libANGLE/capture/capture_gles_3_2_autogen.h"
40 #include "libANGLE/capture/capture_gles_ext_autogen.h"
41 #include "libANGLE/capture/frame_capture_utils.h"
42 #include "libANGLE/capture/gl_enum_utils.h"
43 #include "libANGLE/entry_points_utils.h"
44 #include "libANGLE/queryconversions.h"
45 #include "libANGLE/queryutils.h"
46 #include "third_party/ceval/ceval.h"
47
48 #define USE_SYSTEM_ZLIB
49 #include "compression_utils_portable.h"
50
51 #if !ANGLE_CAPTURE_ENABLED
52 # error Frame capture must be enabled to include this file.
53 #endif // !ANGLE_CAPTURE_ENABLED
54
55 namespace angle
56 {
57 namespace
58 {
59
60 constexpr char kEnabledVarName[] = "ANGLE_CAPTURE_ENABLED";
61 constexpr char kOutDirectoryVarName[] = "ANGLE_CAPTURE_OUT_DIR";
62 constexpr char kFrameStartVarName[] = "ANGLE_CAPTURE_FRAME_START";
63 constexpr char kFrameEndVarName[] = "ANGLE_CAPTURE_FRAME_END";
64 constexpr char kTriggerVarName[] = "ANGLE_CAPTURE_TRIGGER";
65 constexpr char kCaptureLabelVarName[] = "ANGLE_CAPTURE_LABEL";
66 constexpr char kCompressionVarName[] = "ANGLE_CAPTURE_COMPRESSION";
67 constexpr char kSerializeStateVarName[] = "ANGLE_CAPTURE_SERIALIZE_STATE";
68 constexpr char kValidationVarName[] = "ANGLE_CAPTURE_VALIDATION";
69 constexpr char kValidationExprVarName[] = "ANGLE_CAPTURE_VALIDATION_EXPR";
70 constexpr char kTrimEnabledVarName[] = "ANGLE_CAPTURE_TRIM_ENABLED";
71 constexpr char kSourceSizeVarName[] = "ANGLE_CAPTURE_SOURCE_SIZE";
72
73 constexpr size_t kBinaryAlignment = 16;
74 constexpr size_t kFunctionSizeLimit = 5000;
75
76 // Limit based on MSVC Compiler Error C2026
77 constexpr size_t kStringLengthLimit = 16380;
78
79 // Default limit to number of bytes in a capture source files.
80 constexpr size_t kDefaultSourceFileSizeThreshold = 400000;
81
82 // Android debug properties that correspond to the above environment variables
83 constexpr char kAndroidEnabled[] = "debug.angle.capture.enabled";
84 constexpr char kAndroidOutDir[] = "debug.angle.capture.out_dir";
85 constexpr char kAndroidFrameStart[] = "debug.angle.capture.frame_start";
86 constexpr char kAndroidFrameEnd[] = "debug.angle.capture.frame_end";
87 constexpr char kAndroidTrigger[] = "debug.angle.capture.trigger";
88 constexpr char kAndroidCaptureLabel[] = "debug.angle.capture.label";
89 constexpr char kAndroidCompression[] = "debug.angle.capture.compression";
90 constexpr char kAndroidValidation[] = "debug.angle.capture.validation";
91 constexpr char kAndroidValidationExpr[] = "debug.angle.capture.validation_expr";
92 constexpr char kAndroidTrimEnabled[] = "debug.angle.capture.trim_enabled";
93 constexpr char kAndroidSourceSize[] = "debug.angle.capture.source_size";
94
95 struct FramebufferCaptureFuncs
96 {
FramebufferCaptureFuncsangle::__anonf35832ea0111::FramebufferCaptureFuncs97 FramebufferCaptureFuncs(bool isGLES1)
98 {
99 if (isGLES1)
100 {
101 framebufferTexture2D = &gl::CaptureFramebufferTexture2DOES;
102 framebufferRenderbuffer = &gl::CaptureFramebufferRenderbufferOES;
103 bindFramebuffer = &gl::CaptureBindFramebufferOES;
104 genFramebuffers = &gl::CaptureGenFramebuffersOES;
105 bindRenderbuffer = &gl::CaptureBindRenderbufferOES;
106 genRenderbuffers = &gl::CaptureGenRenderbuffersOES;
107 renderbufferStorage = &gl::CaptureRenderbufferStorageOES;
108 }
109 else
110 {
111 framebufferTexture2D = &gl::CaptureFramebufferTexture2D;
112 framebufferRenderbuffer = &gl::CaptureFramebufferRenderbuffer;
113 bindFramebuffer = &gl::CaptureBindFramebuffer;
114 genFramebuffers = &gl::CaptureGenFramebuffers;
115 bindRenderbuffer = &gl::CaptureBindRenderbuffer;
116 genRenderbuffers = &gl::CaptureGenRenderbuffers;
117 renderbufferStorage = &gl::CaptureRenderbufferStorage;
118 }
119 }
120
121 decltype(&gl::CaptureFramebufferTexture2D) framebufferTexture2D;
122 decltype(&gl::CaptureFramebufferRenderbuffer) framebufferRenderbuffer;
123 decltype(&gl::CaptureBindFramebuffer) bindFramebuffer;
124 decltype(&gl::CaptureGenFramebuffers) genFramebuffers;
125 decltype(&gl::CaptureBindRenderbuffer) bindRenderbuffer;
126 decltype(&gl::CaptureGenRenderbuffers) genRenderbuffers;
127 decltype(&gl::CaptureRenderbufferStorage) renderbufferStorage;
128 };
129
GetDefaultOutDirectory()130 std::string GetDefaultOutDirectory()
131 {
132 #if defined(ANGLE_PLATFORM_ANDROID)
133 std::string path = "/sdcard/Android/data/";
134
135 // Linux interface to get application id of the running process
136 FILE *cmdline = fopen("/proc/self/cmdline", "r");
137 char applicationId[512];
138 if (cmdline)
139 {
140 fread(applicationId, 1, sizeof(applicationId), cmdline);
141 fclose(cmdline);
142
143 // Some package may have application id as <app_name>:<cmd_name>
144 char *colonSep = strchr(applicationId, ':');
145 if (colonSep)
146 {
147 *colonSep = '\0';
148 }
149 }
150 else
151 {
152 ERR() << "not able to lookup application id";
153 }
154
155 constexpr char kAndroidOutputSubdir[] = "/angle_capture/";
156 path += std::string(applicationId) + kAndroidOutputSubdir;
157
158 // Check for existence of output path
159 struct stat dir_stat;
160 if (stat(path.c_str(), &dir_stat) == -1)
161 {
162 ERR() << "Output directory '" << path
163 << "' does not exist. Create it over adb using mkdir.";
164 }
165
166 return path;
167 #else
168 return std::string("./");
169 #endif // defined(ANGLE_PLATFORM_ANDROID)
170 }
171
GetCaptureTrigger()172 std::string GetCaptureTrigger()
173 {
174 // Use the GetAndSet variant to improve future lookup times
175 return GetAndSetEnvironmentVarOrUnCachedAndroidProperty(kTriggerVarName, kAndroidTrigger);
176 }
177
operator <<(std::ostream & os,gl::ContextID contextId)178 std::ostream &operator<<(std::ostream &os, gl::ContextID contextId)
179 {
180 os << static_cast<int>(contextId.value);
181 return os;
182 }
183
184 // Used to indicate that "shared" should be used to identify the files.
185 constexpr gl::ContextID kSharedContextId = {0};
186 // Used to indicate no context ID should be output.
187 constexpr gl::ContextID kNoContextId = {std::numeric_limits<uint32_t>::max()};
188
189 struct FmtCapturePrefix
190 {
FmtCapturePrefixangle::__anonf35832ea0111::FmtCapturePrefix191 FmtCapturePrefix(gl::ContextID contextIdIn, const std::string &captureLabelIn)
192 : contextId(contextIdIn), captureLabel(captureLabelIn)
193 {}
194 gl::ContextID contextId;
195 const std::string &captureLabel;
196 };
197
operator <<(std::ostream & os,const FmtCapturePrefix & fmt)198 std::ostream &operator<<(std::ostream &os, const FmtCapturePrefix &fmt)
199 {
200 if (fmt.captureLabel.empty())
201 {
202 os << "angle_capture";
203 }
204 else
205 {
206 os << fmt.captureLabel;
207 }
208
209 if (fmt.contextId == kSharedContextId)
210 {
211 os << "_shared";
212 }
213 else if (fmt.contextId != kNoContextId)
214 {
215 os << "_context" << fmt.contextId;
216 }
217
218 return os;
219 }
220
221 enum class ReplayFunc
222 {
223 Replay,
224 Setup,
225 Reset,
226 };
227
228 constexpr uint32_t kNoPartId = std::numeric_limits<uint32_t>::max();
229
230 struct FmtReplayFunction
231 {
FmtReplayFunctionangle::__anonf35832ea0111::FmtReplayFunction232 FmtReplayFunction(gl::ContextID contextIdIn,
233 uint32_t frameIndexIn,
234 uint32_t partIdIn = kNoPartId)
235 : contextId(contextIdIn), frameIndex(frameIndexIn), partId(partIdIn)
236 {}
237 gl::ContextID contextId;
238 uint32_t frameIndex;
239 uint32_t partId;
240 };
241
operator <<(std::ostream & os,const FmtReplayFunction & fmt)242 std::ostream &operator<<(std::ostream &os, const FmtReplayFunction &fmt)
243 {
244 os << "ReplayContext";
245
246 if (fmt.contextId == kSharedContextId)
247 {
248 os << "Shared";
249 }
250 else
251 {
252 os << fmt.contextId;
253 }
254
255 os << "Frame" << fmt.frameIndex;
256
257 if (fmt.partId != kNoPartId)
258 {
259 os << "Part" << fmt.partId;
260 }
261 os << "()";
262 return os;
263 }
264
265 struct FmtSetupFunction
266 {
FmtSetupFunctionangle::__anonf35832ea0111::FmtSetupFunction267 FmtSetupFunction(uint32_t partIdIn, gl::ContextID contextIdIn)
268 : partId(partIdIn), contextId(contextIdIn)
269 {}
270
271 uint32_t partId;
272 gl::ContextID contextId;
273 };
274
operator <<(std::ostream & os,const FmtSetupFunction & fmt)275 std::ostream &operator<<(std::ostream &os, const FmtSetupFunction &fmt)
276 {
277 os << "SetupReplayContext";
278
279 if (fmt.contextId == kSharedContextId)
280 {
281 os << "Shared";
282 }
283 else
284 {
285 os << fmt.contextId;
286 }
287
288 if (fmt.partId != kNoPartId)
289 {
290 os << "Part" << fmt.partId;
291 }
292 os << "()";
293 return os;
294 }
295
296 struct FmtResetFunction
297 {
FmtResetFunctionangle::__anonf35832ea0111::FmtResetFunction298 FmtResetFunction() {}
299 };
300
operator <<(std::ostream & os,const FmtResetFunction & fmt)301 std::ostream &operator<<(std::ostream &os, const FmtResetFunction &fmt)
302 {
303 os << "ResetReplay()";
304 return os;
305 }
306
307 struct FmtFunction
308 {
FmtFunctionangle::__anonf35832ea0111::FmtFunction309 FmtFunction(ReplayFunc funcTypeIn,
310 gl::ContextID contextIdIn,
311 uint32_t frameIndexIn,
312 uint32_t partIdIn)
313 : funcType(funcTypeIn), contextId(contextIdIn), frameIndex(frameIndexIn), partId(partIdIn)
314 {}
315
316 ReplayFunc funcType;
317 gl::ContextID contextId;
318 uint32_t frameIndex;
319 uint32_t partId;
320 };
321
operator <<(std::ostream & os,const FmtFunction & fmt)322 std::ostream &operator<<(std::ostream &os, const FmtFunction &fmt)
323 {
324 switch (fmt.funcType)
325 {
326 case ReplayFunc::Replay:
327 os << FmtReplayFunction(fmt.contextId, fmt.frameIndex, fmt.partId);
328 break;
329
330 case ReplayFunc::Setup:
331 os << FmtSetupFunction(fmt.partId, fmt.contextId);
332 break;
333
334 case ReplayFunc::Reset:
335 os << FmtResetFunction();
336 break;
337
338 default:
339 UNREACHABLE();
340 break;
341 }
342
343 return os;
344 }
345
346 struct FmtGetSerializedContextStateFunction
347 {
FmtGetSerializedContextStateFunctionangle::__anonf35832ea0111::FmtGetSerializedContextStateFunction348 FmtGetSerializedContextStateFunction(gl::ContextID contextIdIn, uint32_t frameIndexIn)
349 : contextId(contextIdIn), frameIndex(frameIndexIn)
350 {}
351 gl::ContextID contextId;
352 uint32_t frameIndex;
353 };
354
operator <<(std::ostream & os,const FmtGetSerializedContextStateFunction & fmt)355 std::ostream &operator<<(std::ostream &os, const FmtGetSerializedContextStateFunction &fmt)
356 {
357 os << "GetSerializedContext" << fmt.contextId << "StateFrame" << fmt.frameIndex << "Data()";
358 return os;
359 }
360
WriteGLFloatValue(std::ostream & out,GLfloat value)361 void WriteGLFloatValue(std::ostream &out, GLfloat value)
362 {
363 // Check for non-representable values
364 ASSERT(std::numeric_limits<float>::has_infinity);
365 ASSERT(std::numeric_limits<float>::has_quiet_NaN);
366
367 if (std::isinf(value))
368 {
369 float negativeInf = -std::numeric_limits<float>::infinity();
370 if (value == negativeInf)
371 {
372 out << "-";
373 }
374 out << "std::numeric_limits<float>::infinity()";
375 }
376 else if (std::isnan(value))
377 {
378 out << "std::numeric_limits<float>::quiet_NaN()";
379 }
380 else
381 {
382 out << std::setprecision(16);
383 out << value;
384 }
385 }
386
387 template <typename T, typename CastT = T>
WriteInlineData(const std::vector<uint8_t> & vec,std::ostream & out)388 void WriteInlineData(const std::vector<uint8_t> &vec, std::ostream &out)
389 {
390 const T *data = reinterpret_cast<const T *>(vec.data());
391 size_t count = vec.size() / sizeof(T);
392
393 if (data == nullptr)
394 {
395 return;
396 }
397
398 out << static_cast<CastT>(data[0]);
399
400 for (size_t dataIndex = 1; dataIndex < count; ++dataIndex)
401 {
402 out << ", " << static_cast<CastT>(data[dataIndex]);
403 }
404 }
405
406 template <>
WriteInlineData(const std::vector<uint8_t> & vec,std::ostream & out)407 void WriteInlineData<GLchar>(const std::vector<uint8_t> &vec, std::ostream &out)
408 {
409 const GLchar *data = reinterpret_cast<const GLchar *>(vec.data());
410 size_t count = vec.size() / sizeof(GLchar);
411
412 if (data == nullptr || data[0] == '\0')
413 {
414 return;
415 }
416
417 out << "\"";
418
419 for (size_t dataIndex = 0; dataIndex < count; ++dataIndex)
420 {
421 if (data[dataIndex] == '\0')
422 break;
423
424 out << static_cast<GLchar>(data[dataIndex]);
425 }
426
427 out << "\"";
428 }
429
WriteStringParamReplay(ReplayWriter & replayWriter,std::ostream & out,std::ostream & header,const CallCapture & call,const ParamCapture & param,std::vector<uint8_t> * binaryData)430 void WriteStringParamReplay(ReplayWriter &replayWriter,
431 std::ostream &out,
432 std::ostream &header,
433 const CallCapture &call,
434 const ParamCapture ¶m,
435 std::vector<uint8_t> *binaryData)
436 {
437 const std::vector<uint8_t> &data = param.data[0];
438 // null terminate C style string
439 ASSERT(data.size() > 0 && data.back() == '\0');
440 std::string str(data.begin(), data.end() - 1);
441
442 constexpr size_t kMaxInlineStringLength = 20000;
443 if (str.size() > kMaxInlineStringLength)
444 {
445 // Store in binary file if the string is too long.
446 // Round up to 16-byte boundary for cross ABI safety.
447 size_t offset = rx::roundUpPow2(binaryData->size(), kBinaryAlignment);
448 binaryData->resize(offset + str.size() + 1);
449 memcpy(binaryData->data() + offset, str.data(), str.size() + 1);
450 out << "reinterpret_cast<const char *>(&gBinaryData[" << offset << "])";
451 }
452 else if (str.find('\n') != std::string::npos)
453 {
454 std::string varName = replayWriter.getInlineVariableName(call.entryPoint, param.name);
455 header << "const char " << varName << "[] = R\"(" << str << ")\";\n";
456 out << varName;
457 }
458 else
459 {
460 out << "\"" << str << "\"";
461 }
462 }
463
WriteStringPointerParamReplay(ReplayWriter & replayWriter,std::ostream & out,std::ostream & header,const CallCapture & call,const ParamCapture & param)464 void WriteStringPointerParamReplay(ReplayWriter &replayWriter,
465 std::ostream &out,
466 std::ostream &header,
467 const CallCapture &call,
468 const ParamCapture ¶m)
469 {
470 // Concatenate the strings to ensure we get an accurate counter
471 std::vector<std::string> strings;
472 for (const std::vector<uint8_t> &data : param.data)
473 {
474 // null terminate C style string
475 ASSERT(data.size() > 0 && data.back() == '\0');
476 strings.emplace_back(data.begin(), data.end() - 1);
477 }
478
479 bool isNewEntry = false;
480 std::string varName = replayWriter.getInlineStringSetVariableName(call.entryPoint, param.name,
481 strings, &isNewEntry);
482
483 if (isNewEntry)
484 {
485 header << "const char *const " << varName << "[] = { \n";
486
487 for (const std::string &str : strings)
488 {
489 // Break up long strings for MSVC
490 size_t copyLength = 0;
491 std::string separator;
492 for (size_t i = 0; i < str.length(); i += kStringLengthLimit)
493 {
494 if ((str.length() - i) <= kStringLengthLimit)
495 {
496 copyLength = str.length() - i;
497 separator = ",";
498 }
499 else
500 {
501 copyLength = kStringLengthLimit;
502 separator = "";
503 }
504
505 header << " R\"(" << str.substr(i, copyLength) << ")\"" << separator << "\n";
506 }
507 }
508
509 header << "};\n";
510 }
511
512 out << varName;
513 }
514
515 template <typename ParamT>
WriteResourceIDPointerParamReplay(ReplayWriter & replayWriter,std::ostream & out,std::ostream & header,const CallCapture & call,const ParamCapture & param)516 void WriteResourceIDPointerParamReplay(ReplayWriter &replayWriter,
517 std::ostream &out,
518 std::ostream &header,
519 const CallCapture &call,
520 const ParamCapture ¶m)
521 {
522 std::string varName = replayWriter.getInlineVariableName(call.entryPoint, param.name);
523
524 header << "const GLuint " << varName << "[] = { ";
525
526 const ResourceIDType resourceIDType = GetResourceIDTypeFromParamType(param.type);
527 ASSERT(resourceIDType != ResourceIDType::InvalidEnum);
528 const char *name = GetResourceIDTypeName(resourceIDType);
529
530 if (param.dataNElements > 0)
531 {
532 ASSERT(param.data.size() == 1);
533
534 const ParamT *returnedIDs = reinterpret_cast<const ParamT *>(param.data[0].data());
535 for (GLsizei resIndex = 0; resIndex < param.dataNElements; ++resIndex)
536 {
537 ParamT id = returnedIDs[resIndex];
538 if (resIndex > 0)
539 {
540 header << ", ";
541 }
542 header << "g" << name << "Map[" << id.value << "]";
543 }
544 }
545 else
546 {
547 header << "0";
548 }
549 header << " };\n ";
550
551 out << varName;
552 }
553
WriteBinaryParamReplay(ReplayWriter & replayWriter,std::ostream & out,std::ostream & header,const CallCapture & call,const ParamCapture & param,std::vector<uint8_t> * binaryData)554 void WriteBinaryParamReplay(ReplayWriter &replayWriter,
555 std::ostream &out,
556 std::ostream &header,
557 const CallCapture &call,
558 const ParamCapture ¶m,
559 std::vector<uint8_t> *binaryData)
560 {
561 std::string varName = replayWriter.getInlineVariableName(call.entryPoint, param.name);
562
563 ASSERT(param.data.size() == 1);
564 const std::vector<uint8_t> &data = param.data[0];
565
566 ParamType overrideType = param.type;
567 if (param.type == ParamType::TGLvoidConstPointer || param.type == ParamType::TvoidConstPointer)
568 {
569 overrideType = ParamType::TGLubyteConstPointer;
570 }
571 if (overrideType == ParamType::TGLenumConstPointer || overrideType == ParamType::TGLcharPointer)
572 {
573 // Inline if data are of type string or enum
574 std::string paramTypeString = ParamTypeToString(param.type);
575 header << paramTypeString.substr(0, paramTypeString.length() - 1) << varName << "[] = { ";
576 if (overrideType == ParamType::TGLenumConstPointer)
577 {
578 WriteInlineData<GLuint>(data, header);
579 }
580 else
581 {
582 ASSERT(overrideType == ParamType::TGLcharPointer);
583 WriteInlineData<GLchar>(data, header);
584 }
585 header << " };\n";
586 out << varName;
587 }
588 else
589 {
590 // Store in binary file if data are not of type string or enum
591 // Round up to 16-byte boundary for cross ABI safety
592 size_t offset = rx::roundUpPow2(binaryData->size(), kBinaryAlignment);
593 binaryData->resize(offset + data.size());
594 memcpy(binaryData->data() + offset, data.data(), data.size());
595 out << "reinterpret_cast<" << ParamTypeToString(overrideType) << ">(&gBinaryData[" << offset
596 << "])";
597 }
598 }
599
SyncIndexValue(GLsync sync)600 uintptr_t SyncIndexValue(GLsync sync)
601 {
602 return reinterpret_cast<uintptr_t>(sync);
603 }
604
WriteCppReplayForCall(const CallCapture & call,ReplayWriter & replayWriter,std::ostream & out,std::ostream & header,std::vector<uint8_t> * binaryData)605 void WriteCppReplayForCall(const CallCapture &call,
606 ReplayWriter &replayWriter,
607 std::ostream &out,
608 std::ostream &header,
609 std::vector<uint8_t> *binaryData)
610 {
611 std::ostringstream callOut;
612
613 if (call.entryPoint == EntryPoint::GLCreateShader ||
614 call.entryPoint == EntryPoint::GLCreateProgram ||
615 call.entryPoint == EntryPoint::GLCreateShaderProgramv)
616 {
617 GLuint id = call.params.getReturnValue().value.GLuintVal;
618 callOut << "gShaderProgramMap[" << id << "] = ";
619 }
620
621 if (call.entryPoint == EntryPoint::GLFenceSync)
622 {
623 GLsync sync = call.params.getReturnValue().value.GLsyncVal;
624 callOut << "gSyncMap[" << SyncIndexValue(sync) << "] = ";
625 }
626
627 // Depending on how a buffer is mapped, we may need to track its location for readback
628 bool trackBufferPointer = false;
629
630 if (call.entryPoint == EntryPoint::GLMapBufferRange ||
631 call.entryPoint == EntryPoint::GLMapBufferRangeEXT)
632 {
633 GLbitfield access =
634 call.params.getParam("access", ParamType::TGLbitfield, 3).value.GLbitfieldVal;
635
636 trackBufferPointer = access & GL_MAP_WRITE_BIT;
637 }
638
639 if (call.entryPoint == EntryPoint::GLMapBuffer || call.entryPoint == EntryPoint::GLMapBufferOES)
640 {
641 GLenum access = call.params.getParam("access", ParamType::TGLenum, 1).value.GLenumVal;
642
643 trackBufferPointer =
644 access == GL_WRITE_ONLY_OES || access == GL_WRITE_ONLY || access == GL_READ_WRITE;
645 }
646
647 if (trackBufferPointer)
648 {
649 // Track the returned pointer so we update its data when unmapped
650 gl::BufferID bufferID = call.params.getMappedBufferID();
651 callOut << "gMappedBufferData[";
652 WriteParamValueReplay<ParamType::TBufferID>(callOut, call, bufferID);
653 callOut << "] = ";
654 }
655
656 callOut << call.name() << "(";
657
658 bool first = true;
659 for (const ParamCapture ¶m : call.params.getParamCaptures())
660 {
661 if (!first)
662 {
663 callOut << ", ";
664 }
665
666 if (param.arrayClientPointerIndex != -1 && param.value.voidConstPointerVal != nullptr)
667 {
668 callOut << "gClientArrays[" << param.arrayClientPointerIndex << "]";
669 }
670 else if (param.readBufferSizeBytes > 0)
671 {
672 callOut << "reinterpret_cast<" << ParamTypeToString(param.type) << ">(gReadBuffer)";
673 }
674 else if (param.data.empty())
675 {
676 if (param.type == ParamType::TGLenum)
677 {
678 OutputGLenumString(callOut, param.enumGroup, param.value.GLenumVal);
679 }
680 else if (param.type == ParamType::TGLbitfield)
681 {
682 OutputGLbitfieldString(callOut, param.enumGroup, param.value.GLbitfieldVal);
683 }
684 else if (param.type == ParamType::TGLfloat)
685 {
686 WriteGLFloatValue(callOut, param.value.GLfloatVal);
687 }
688 else if (param.type == ParamType::TGLsync)
689 {
690 callOut << "gSyncMap[" << SyncIndexValue(param.value.GLsyncVal) << "]";
691 }
692 else if (param.type == ParamType::TGLuint64 && param.name == "timeout")
693 {
694 if (param.value.GLuint64Val == GL_TIMEOUT_IGNORED)
695 {
696 callOut << "GL_TIMEOUT_IGNORED";
697 }
698 else
699 {
700 WriteParamCaptureReplay(callOut, call, param);
701 }
702 }
703 else
704 {
705 WriteParamCaptureReplay(callOut, call, param);
706 }
707 }
708 else
709 {
710 switch (param.type)
711 {
712 case ParamType::TGLcharConstPointer:
713 WriteStringParamReplay(replayWriter, callOut, header, call, param, binaryData);
714 break;
715 case ParamType::TGLcharConstPointerPointer:
716 WriteStringPointerParamReplay(replayWriter, callOut, header, call, param);
717 break;
718 case ParamType::TBufferIDConstPointer:
719 WriteResourceIDPointerParamReplay<gl::BufferID>(replayWriter, callOut, out,
720 call, param);
721 break;
722 case ParamType::TFenceNVIDConstPointer:
723 WriteResourceIDPointerParamReplay<gl::FenceNVID>(replayWriter, callOut, out,
724 call, param);
725 break;
726 case ParamType::TFramebufferIDConstPointer:
727 WriteResourceIDPointerParamReplay<gl::FramebufferID>(replayWriter, callOut, out,
728 call, param);
729 break;
730 case ParamType::TMemoryObjectIDConstPointer:
731 WriteResourceIDPointerParamReplay<gl::MemoryObjectID>(replayWriter, callOut,
732 out, call, param);
733 break;
734 case ParamType::TProgramPipelineIDConstPointer:
735 WriteResourceIDPointerParamReplay<gl::ProgramPipelineID>(replayWriter, callOut,
736 out, call, param);
737 break;
738 case ParamType::TQueryIDConstPointer:
739 WriteResourceIDPointerParamReplay<gl::QueryID>(replayWriter, callOut, out, call,
740 param);
741 break;
742 case ParamType::TRenderbufferIDConstPointer:
743 WriteResourceIDPointerParamReplay<gl::RenderbufferID>(replayWriter, callOut,
744 out, call, param);
745 break;
746 case ParamType::TSamplerIDConstPointer:
747 WriteResourceIDPointerParamReplay<gl::SamplerID>(replayWriter, callOut, out,
748 call, param);
749 break;
750 case ParamType::TSemaphoreIDConstPointer:
751 WriteResourceIDPointerParamReplay<gl::SemaphoreID>(replayWriter, callOut, out,
752 call, param);
753 break;
754 case ParamType::TTextureIDConstPointer:
755 WriteResourceIDPointerParamReplay<gl::TextureID>(replayWriter, callOut, out,
756 call, param);
757 break;
758 case ParamType::TTransformFeedbackIDConstPointer:
759 WriteResourceIDPointerParamReplay<gl::TransformFeedbackID>(
760 replayWriter, callOut, out, call, param);
761 break;
762 case ParamType::TVertexArrayIDConstPointer:
763 WriteResourceIDPointerParamReplay<gl::VertexArrayID>(replayWriter, callOut, out,
764 call, param);
765 break;
766 default:
767 WriteBinaryParamReplay(replayWriter, callOut, header, call, param, binaryData);
768 break;
769 }
770 }
771
772 first = false;
773 }
774
775 callOut << ")";
776
777 out << callOut.str();
778 }
779
MaxClientArraySize(const gl::AttribArray<size_t> & clientArraySizes)780 size_t MaxClientArraySize(const gl::AttribArray<size_t> &clientArraySizes)
781 {
782 size_t found = 0;
783 for (size_t size : clientArraySizes)
784 {
785 if (size > found)
786 {
787 found = size;
788 }
789 }
790
791 return found;
792 }
793
CaptureMakeCurrent(EGLDisplay display,EGLSurface draw,EGLSurface read,EGLContext context)794 CallCapture CaptureMakeCurrent(EGLDisplay display,
795 EGLSurface draw,
796 EGLSurface read,
797 EGLContext context)
798 {
799 ParamBuffer paramBuffer;
800 paramBuffer.addValueParam("display", ParamType::TEGLDisplay, display);
801 paramBuffer.addValueParam("draw", ParamType::TEGLSurface, draw);
802 paramBuffer.addValueParam("read", ParamType::TEGLSurface, read);
803 paramBuffer.addValueParam("context", ParamType::TEGLContext, context);
804
805 return CallCapture(angle::EntryPoint::EGLMakeCurrent, std::move(paramBuffer));
806 }
807
GetBinaryDataFilePath(bool compression,const std::string & captureLabel)808 std::string GetBinaryDataFilePath(bool compression, const std::string &captureLabel)
809 {
810 std::stringstream fnameStream;
811 fnameStream << FmtCapturePrefix(kNoContextId, captureLabel) << ".angledata";
812 if (compression)
813 {
814 fnameStream << ".gz";
815 }
816 return fnameStream.str();
817 }
818
SaveBinaryData(bool compression,const std::string & outDir,gl::ContextID contextId,const std::string & captureLabel,const std::vector<uint8_t> & binaryData)819 void SaveBinaryData(bool compression,
820 const std::string &outDir,
821 gl::ContextID contextId,
822 const std::string &captureLabel,
823 const std::vector<uint8_t> &binaryData)
824 {
825 std::string binaryDataFileName = GetBinaryDataFilePath(compression, captureLabel);
826 std::string dataFilepath = outDir + binaryDataFileName;
827
828 SaveFileHelper saveData(dataFilepath);
829
830 if (compression)
831 {
832 // Save compressed data.
833 uLong uncompressedSize = static_cast<uLong>(binaryData.size());
834 uLong expectedCompressedSize = zlib_internal::GzipExpectedCompressedSize(uncompressedSize);
835
836 std::vector<uint8_t> compressedData(expectedCompressedSize, 0);
837
838 uLong compressedSize = expectedCompressedSize;
839 int zResult = zlib_internal::GzipCompressHelper(compressedData.data(), &compressedSize,
840 binaryData.data(), uncompressedSize,
841 nullptr, nullptr);
842
843 if (zResult != Z_OK)
844 {
845 FATAL() << "Error compressing binary data: " << zResult;
846 }
847
848 saveData.write(compressedData.data(), compressedSize);
849 }
850 else
851 {
852 saveData.write(binaryData.data(), binaryData.size());
853 }
854 }
855
WriteInitReplayCall(bool compression,std::ostream & out,gl::ContextID contextId,const std::string & captureLabel,size_t maxClientArraySize,size_t readBufferSize,const PackedEnumMap<ResourceIDType,uint32_t> & maxIDs)856 void WriteInitReplayCall(bool compression,
857 std::ostream &out,
858 gl::ContextID contextId,
859 const std::string &captureLabel,
860 size_t maxClientArraySize,
861 size_t readBufferSize,
862 const PackedEnumMap<ResourceIDType, uint32_t> &maxIDs)
863 {
864 for (ResourceIDType resourceID : AllEnums<ResourceIDType>())
865 {
866 const char *name = GetResourceIDTypeName(resourceID);
867 out << " uint32_t kMax" << name << " = " << maxIDs[resourceID] << ";\n";
868 }
869
870 std::string binaryDataFileName = GetBinaryDataFilePath(compression, captureLabel);
871 out << " InitializeReplay(\"" << binaryDataFileName << "\", " << maxClientArraySize << ", "
872 << readBufferSize;
873
874 for (ResourceIDType resourceID : AllEnums<ResourceIDType>())
875 {
876 out << ", kMax" << GetResourceIDTypeName(resourceID);
877 }
878
879 out << ");\n";
880 }
881
882 // TODO (http://anglebug.com/4599): Reset more state on frame loop
MaybeResetResources(ResourceIDType resourceIDType,ReplayWriter & replayWriter,std::stringstream & out,std::stringstream & header,ResourceTracker * resourceTracker,std::vector<uint8_t> * binaryData)883 void MaybeResetResources(ResourceIDType resourceIDType,
884 ReplayWriter &replayWriter,
885 std::stringstream &out,
886 std::stringstream &header,
887 ResourceTracker *resourceTracker,
888 std::vector<uint8_t> *binaryData)
889 {
890 // Local helper to get well structured blocks in Delete calls, i.e.
891 // const GLuint deleteTextures[] = {
892 // gTextureMap[1], gTextureMap[2], gTextureMap[3], gTextureMap[4],
893 // gTextureMap[5], gTextureMap[6], gTextureMap[7], gTextureMap[8]};
894 auto formatResourceIndex = [](std::stringstream &out, size_t i) {
895 if (i > 0)
896 {
897 out << ", ";
898 }
899 if ((i % 4) == 0)
900 {
901 out << "\n ";
902 }
903 };
904
905 switch (resourceIDType)
906 {
907 case ResourceIDType::Buffer:
908 {
909 TrackedResource &trackedBuffers =
910 resourceTracker->getTrackedResource(ResourceIDType::Buffer);
911 ResourceSet &newBuffers = trackedBuffers.getNewResources();
912 ResourceSet &buffersToRegen = trackedBuffers.getResourcesToRegen();
913 ResourceCalls &bufferRegenCalls = trackedBuffers.getResourceRegenCalls();
914 ResourceCalls &bufferRestoreCalls = trackedBuffers.getResourceRestoreCalls();
915
916 BufferCalls &bufferMapCalls = resourceTracker->getBufferMapCalls();
917 BufferCalls &bufferUnmapCalls = resourceTracker->getBufferUnmapCalls();
918
919 // If we have any new buffers generated and not deleted during the run, or any buffers
920 // that we need to regen, delete them now
921 if (!newBuffers.empty() || !buffersToRegen.empty())
922 {
923 size_t count = 0;
924
925 out << " const GLuint deleteBuffers[] = {";
926
927 for (auto &oldBuffer : buffersToRegen)
928 {
929 formatResourceIndex(out, count);
930 out << "gBufferMap[" << oldBuffer << "]";
931 ++count;
932 }
933
934 for (auto &newBuffer : newBuffers)
935 {
936 formatResourceIndex(out, count);
937 out << "gBufferMap[" << newBuffer << "]";
938 ++count;
939 }
940
941 // Delete all the new and old buffers at once
942 out << "};\n";
943 out << " glDeleteBuffers(" << count << ", deleteBuffers);\n";
944 }
945
946 // If any of our starting buffers were deleted during the run, recreate them
947 for (GLuint id : buffersToRegen)
948 {
949 // Emit their regen calls
950 for (CallCapture &call : bufferRegenCalls[id])
951 {
952 out << " ";
953 WriteCppReplayForCall(call, replayWriter, out, header, binaryData);
954 out << ";\n";
955 }
956 }
957
958 // If any of our starting buffers were modified during the run, restore their contents
959 ResourceSet &buffersToRestore = trackedBuffers.getResourcesToRestore();
960 for (GLuint id : buffersToRestore)
961 {
962 if (resourceTracker->getStartingBuffersMappedCurrent(id))
963 {
964 // Some drivers require the buffer to be unmapped before you can update data,
965 // which violates the spec. See gl::Buffer::bufferDataImpl().
966 for (CallCapture &call : bufferUnmapCalls[id])
967 {
968 out << " ";
969 WriteCppReplayForCall(call, replayWriter, out, header, binaryData);
970 out << ";\n";
971 }
972 }
973
974 // Emit their restore calls
975 for (CallCapture &call : bufferRestoreCalls[id])
976 {
977 out << " ";
978 WriteCppReplayForCall(call, replayWriter, out, header, binaryData);
979 out << ";\n";
980
981 // Also note that this buffer has been implicitly unmapped by this call
982 resourceTracker->setBufferUnmapped(id);
983 }
984 }
985
986 // Update the map/unmap of buffers to match the starting state
987 ResourceSet startingBuffers = trackedBuffers.getStartingResources();
988 for (GLuint id : startingBuffers)
989 {
990 // If the buffer was mapped at the start, but is not mapped now, we need to map
991 if (resourceTracker->getStartingBuffersMappedInitial(id) &&
992 !resourceTracker->getStartingBuffersMappedCurrent(id))
993 {
994 // Emit their map calls
995 for (CallCapture &call : bufferMapCalls[id])
996 {
997 out << " ";
998 WriteCppReplayForCall(call, replayWriter, out, header, binaryData);
999 out << ";\n";
1000 }
1001 }
1002 // If the buffer was unmapped at the start, but is mapped now, we need to unmap
1003 if (!resourceTracker->getStartingBuffersMappedInitial(id) &&
1004 resourceTracker->getStartingBuffersMappedCurrent(id))
1005 {
1006 // Emit their unmap calls
1007 for (CallCapture &call : bufferUnmapCalls[id])
1008 {
1009 out << " ";
1010 WriteCppReplayForCall(call, replayWriter, out, header, binaryData);
1011 out << ";\n";
1012 }
1013 }
1014 }
1015
1016 // Restore buffer bindings as seen during MEC
1017 std::vector<CallCapture> &bufferBindingCalls = resourceTracker->getBufferBindingCalls();
1018 for (CallCapture &call : bufferBindingCalls)
1019 {
1020 out << " ";
1021 WriteCppReplayForCall(call, replayWriter, out, header, binaryData);
1022 out << ";\n";
1023 }
1024
1025 break;
1026 }
1027 case ResourceIDType::Framebuffer:
1028 {
1029 TrackedResource &trackedFramebuffers =
1030 resourceTracker->getTrackedResource(ResourceIDType::Framebuffer);
1031 ResourceSet &newFramebuffers = trackedFramebuffers.getNewResources();
1032 ResourceSet &framebuffersToRegen = trackedFramebuffers.getResourcesToRegen();
1033 ResourceCalls &framebufferRegenCalls = trackedFramebuffers.getResourceRegenCalls();
1034 ResourceCalls &framebufferRestoreCalls = trackedFramebuffers.getResourceRestoreCalls();
1035
1036 // If we have any new framebuffers generated and not deleted during the run, or any
1037 // framebuffers that we need to regen, delete them now
1038 if (!newFramebuffers.empty() || !framebuffersToRegen.empty())
1039 {
1040 size_t count = 0;
1041
1042 out << " const GLuint deleteFramebuffers[] = {";
1043
1044 for (auto &oldFb : framebuffersToRegen)
1045 {
1046 formatResourceIndex(out, count);
1047 out << "gFramebufferMap[" << oldFb << "]";
1048 ++count;
1049 }
1050
1051 for (auto &newFb : newFramebuffers)
1052 {
1053 formatResourceIndex(out, count);
1054 out << "gFramebufferMap[" << newFb << "]";
1055 ++count;
1056 }
1057
1058 // Delete all the new and old framebuffers at once
1059 out << "};\n";
1060 out << " glDeleteFramebuffers(" << count << ", deleteFramebuffers);\n";
1061 }
1062
1063 for (GLuint id : framebuffersToRegen)
1064 {
1065 // Emit their regen calls
1066 for (CallCapture &call : framebufferRegenCalls[id])
1067 {
1068 out << " ";
1069 WriteCppReplayForCall(call, replayWriter, out, header, binaryData);
1070 out << ";\n";
1071 }
1072 }
1073
1074 // If any of our starting framebuffers were modified during the run, restore their
1075 // contents
1076 ResourceSet &framebuffersToRestore = trackedFramebuffers.getResourcesToRestore();
1077 for (GLuint id : framebuffersToRestore)
1078 {
1079 // Emit their restore calls
1080 for (CallCapture &call : framebufferRestoreCalls[id])
1081 {
1082 out << " ";
1083 WriteCppReplayForCall(call, replayWriter, out, header, binaryData);
1084 out << ";\n";
1085 }
1086 }
1087 break;
1088 }
1089 case ResourceIDType::Renderbuffer:
1090 {
1091 ResourceSet &newRenderbuffers =
1092 resourceTracker->getTrackedResource(ResourceIDType::Renderbuffer).getNewResources();
1093
1094 // Delete any new renderbuffers generated and not deleted during the run
1095 if (!newRenderbuffers.empty())
1096 {
1097 size_t count = 0;
1098
1099 out << " const GLuint deleteRenderbuffers[] = {";
1100
1101 for (auto &newRb : newRenderbuffers)
1102 {
1103 formatResourceIndex(out, count);
1104 out << "gRenderbufferMap[" << newRb << "]";
1105 ++count;
1106 }
1107
1108 out << "};\n";
1109 out << " glDeleteRenderbuffers(" << count << ", deleteRenderbuffers);\n";
1110 }
1111
1112 // TODO (http://anglebug.com/4599): Handle renderbuffers that need regen
1113 // This would only happen if a starting renderbuffer was deleted during the run.
1114 ASSERT(resourceTracker->getTrackedResource(ResourceIDType::Renderbuffer)
1115 .getResourcesToRegen()
1116 .empty());
1117 break;
1118 }
1119 case ResourceIDType::ShaderProgram:
1120 {
1121 ResourceSet &newPrograms =
1122 resourceTracker->getTrackedResource(ResourceIDType::ShaderProgram)
1123 .getNewResources();
1124
1125 // If we have any new programs created and not deleted during the run, delete them now
1126 for (const GLuint &newProgram : newPrograms)
1127 {
1128 out << " glDeleteProgram(gShaderProgramMap[" << newProgram << "]);\n";
1129 }
1130
1131 // TODO (http://anglebug.com/5968): Handle programs that need regen
1132 // This would only happen if a starting program was deleted during the run
1133 ASSERT(resourceTracker->getTrackedResource(ResourceIDType::ShaderProgram)
1134 .getResourcesToRegen()
1135 .empty());
1136 break;
1137 }
1138 case ResourceIDType::Texture:
1139 {
1140 TrackedResource &trackedTextures =
1141 resourceTracker->getTrackedResource(ResourceIDType::Texture);
1142 ResourceSet &newTextures = trackedTextures.getNewResources();
1143 ResourceSet &texturesToRegen = trackedTextures.getResourcesToRegen();
1144 ResourceCalls &textureRegenCalls = trackedTextures.getResourceRegenCalls();
1145 ResourceCalls &textureRestoreCalls = trackedTextures.getResourceRestoreCalls();
1146
1147 // If we have any new textures generated and not deleted during the run, or any textures
1148 // modified during the run that we need to regen, delete them now
1149 if (!newTextures.empty() || !texturesToRegen.empty())
1150 {
1151 size_t count = 0;
1152
1153 out << " const GLuint deleteTextures[] = {";
1154
1155 for (auto &oldTex : texturesToRegen)
1156 {
1157 formatResourceIndex(out, count);
1158 out << "gTextureMap[" << oldTex << "]";
1159 ++count;
1160 }
1161
1162 for (auto &newTex : newTextures)
1163 {
1164 formatResourceIndex(out, count);
1165 out << "gTextureMap[" << newTex << "]";
1166 ++count;
1167 }
1168
1169 out << "};\n";
1170 out << " glDeleteTextures(" << count << ", deleteTextures);\n";
1171 }
1172
1173 // If any of our starting textures were deleted, regen them
1174 for (GLuint id : texturesToRegen)
1175 {
1176 // Emit their regen calls
1177 for (CallCapture &call : textureRegenCalls[id])
1178 {
1179 out << " ";
1180 WriteCppReplayForCall(call, replayWriter, out, header, binaryData);
1181 out << ";\n";
1182 }
1183 }
1184
1185 // If any of our starting textures were modified during the run, restore their contents
1186 ResourceSet &texturesToRestore = trackedTextures.getResourcesToRestore();
1187 for (GLuint id : texturesToRestore)
1188 {
1189 // Emit their restore calls
1190 for (CallCapture &call : textureRestoreCalls[id])
1191 {
1192 out << " ";
1193 WriteCppReplayForCall(call, replayWriter, out, header, binaryData);
1194 out << ";\n";
1195 }
1196 }
1197 break;
1198 }
1199 case ResourceIDType::VertexArray:
1200 {
1201 ResourceSet &newVertextArrays =
1202 resourceTracker->getTrackedResource(ResourceIDType::VertexArray).getNewResources();
1203
1204 // If we have any new vertex arrays generated and not deleted during the run, delete
1205 // them now
1206 if (!newVertextArrays.empty())
1207 {
1208 size_t count = 0;
1209
1210 out << " const GLuint deleteVertexArrays[] = {";
1211
1212 for (auto &newVA : newVertextArrays)
1213 {
1214 formatResourceIndex(out, count);
1215 out << "gVertexArrayMap[" << newVA << "]";
1216 ++count;
1217 }
1218
1219 out << "};\n";
1220 out << " glDeleteVertexArrays(" << count << ", deleteVertexArrays);\n";
1221 }
1222
1223 // TODO (http://anglebug.com/4599): Handle vertex arrays that need regen
1224 // This would only happen if a starting vertex array was deleted during the run.
1225 ASSERT(resourceTracker->getTrackedResource(ResourceIDType::VertexArray)
1226 .getResourcesToRegen()
1227 .empty());
1228 break;
1229 }
1230 default:
1231 // TODO (http://anglebug.com/4599): Reset more than just buffers
1232 break;
1233 }
1234 }
1235
MaybeResetFenceSyncObjects(std::stringstream & out,ReplayWriter & replayWriter,std::stringstream & header,ResourceTracker * resourceTracker,std::vector<uint8_t> * binaryData)1236 void MaybeResetFenceSyncObjects(std::stringstream &out,
1237 ReplayWriter &replayWriter,
1238 std::stringstream &header,
1239 ResourceTracker *resourceTracker,
1240 std::vector<uint8_t> *binaryData)
1241 {
1242 FenceSyncCalls &fenceSyncRegenCalls = resourceTracker->getFenceSyncRegenCalls();
1243
1244 // If any of our starting fence sync objects were deleted during the run, recreate them
1245 FenceSyncSet &fenceSyncsToRegen = resourceTracker->getFenceSyncsToRegen();
1246 for (const GLsync sync : fenceSyncsToRegen)
1247 {
1248 // Emit their regen calls
1249 for (CallCapture &call : fenceSyncRegenCalls[sync])
1250 {
1251 out << " ";
1252 WriteCppReplayForCall(call, replayWriter, out, header, binaryData);
1253 out << ";\n";
1254 }
1255 }
1256 }
1257
MaybeResetOpaqueTypeObjects(ReplayWriter & replayWriter,std::stringstream & out,std::stringstream & header,ResourceTracker * resourceTracker,std::vector<uint8_t> * binaryData)1258 void MaybeResetOpaqueTypeObjects(ReplayWriter &replayWriter,
1259 std::stringstream &out,
1260 std::stringstream &header,
1261 ResourceTracker *resourceTracker,
1262 std::vector<uint8_t> *binaryData)
1263 {
1264 MaybeResetFenceSyncObjects(out, replayWriter, header, resourceTracker, binaryData);
1265 }
1266
FindShaderProgramIDsInCall(const CallCapture & call,std::vector<gl::ShaderProgramID> & idsOut)1267 bool FindShaderProgramIDsInCall(const CallCapture &call, std::vector<gl::ShaderProgramID> &idsOut)
1268 {
1269 for (const ParamCapture ¶m : call.params.getParamCaptures())
1270 {
1271 // Only checking for programs right now, but could be expanded to all ResourceTypes
1272 if (param.type == ParamType::TShaderProgramID)
1273 {
1274 idsOut.push_back(param.value.ShaderProgramIDVal);
1275 }
1276 }
1277
1278 return !idsOut.empty();
1279 }
1280
MarkResourceIDActive(ResourceIDType resourceType,GLuint id,std::vector<CallCapture> * setupCalls,const ResourceIDToSetupCallsMap * resourceIDToSetupCallsMap)1281 void MarkResourceIDActive(ResourceIDType resourceType,
1282 GLuint id,
1283 std::vector<CallCapture> *setupCalls,
1284 const ResourceIDToSetupCallsMap *resourceIDToSetupCallsMap)
1285 {
1286 const std::map<GLuint, gl::Range<size_t>> &resourceSetupCalls =
1287 (*resourceIDToSetupCallsMap)[resourceType];
1288 const auto iter = resourceSetupCalls.find(id);
1289 if (iter == resourceSetupCalls.end())
1290 {
1291 return;
1292 }
1293
1294 // Mark all of the calls that were used to initialize this resource as ACTIVE
1295 const gl::Range<size_t> &calls = iter->second;
1296 for (size_t index : calls)
1297 {
1298 (*setupCalls)[index].isActive = true;
1299 }
1300 }
1301
1302 // Some replay functions can get quite large. If over a certain size, this method breaks up the
1303 // 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)1304 void WriteCppReplayFunctionWithParts(const gl::ContextID contextID,
1305 ReplayFunc replayFunc,
1306 ReplayWriter &replayWriter,
1307 uint32_t frameIndex,
1308 std::vector<uint8_t> *binaryData,
1309 const std::vector<CallCapture> &calls,
1310 std::stringstream &header,
1311 std::stringstream &out)
1312 {
1313 int callCount = 0;
1314 int partCount = 0;
1315
1316 if (calls.size() > kFunctionSizeLimit)
1317 {
1318 out << "void " << FmtFunction(replayFunc, contextID, frameIndex, ++partCount) << "\n";
1319 }
1320 else
1321 {
1322 out << "void " << FmtFunction(replayFunc, contextID, frameIndex, kNoPartId) << "\n";
1323 }
1324
1325 out << "{\n";
1326
1327 for (const CallCapture &call : calls)
1328 {
1329 if (!call.isActive)
1330 {
1331 // Don't write setup calls for inactive resources
1332 continue;
1333 }
1334
1335 out << " ";
1336 WriteCppReplayForCall(call, replayWriter, out, header, binaryData);
1337 out << ";\n";
1338
1339 if (partCount > 0 && ++callCount % kFunctionSizeLimit == 0)
1340 {
1341 out << "}\n";
1342 out << "\n";
1343 out << "void " << FmtFunction(replayFunc, contextID, frameIndex, ++partCount) << "\n";
1344 out << "{\n";
1345 }
1346 }
1347 out << "}\n";
1348
1349 if (partCount > 0)
1350 {
1351 out << "\n";
1352 out << "void " << FmtFunction(replayFunc, contextID, frameIndex, kNoPartId) << "\n";
1353 out << "{\n";
1354
1355 // Write out the main call which calls all the parts.
1356 for (int i = 1; i <= partCount; i++)
1357 {
1358 out << " " << FmtFunction(replayFunc, contextID, frameIndex, i) << ";\n";
1359 }
1360
1361 out << "}\n";
1362 }
1363 }
1364
1365 // Auxiliary contexts are other contexts in the share group that aren't the context calling
1366 // 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)1367 void WriteAuxiliaryContextCppSetupReplay(ReplayWriter &replayWriter,
1368 bool compression,
1369 const std::string &outDir,
1370 const gl::Context *context,
1371 const std::string &captureLabel,
1372 uint32_t frameIndex,
1373 const std::vector<CallCapture> &setupCalls,
1374 std::vector<uint8_t> *binaryData,
1375 bool serializeStateEnabled,
1376 const FrameCaptureShared &frameCaptureShared)
1377 {
1378 ASSERT(frameCaptureShared.getWindowSurfaceContextID() != context->id());
1379
1380 {
1381 std::stringstream filenameStream;
1382 filenameStream << outDir << FmtCapturePrefix(context->id(), captureLabel);
1383 std::string filenamePattern = filenameStream.str();
1384 replayWriter.setFilenamePattern(filenamePattern);
1385 }
1386
1387 {
1388 std::stringstream include;
1389 include << "#include \""
1390 << FmtCapturePrefix(frameCaptureShared.getWindowSurfaceContextID(), captureLabel)
1391 << ".h\"\n";
1392 include << "#include \"angle_trace_gl.h\"\n";
1393
1394 std::string frameIncludes = include.str();
1395 replayWriter.setSourcePrologue(frameIncludes);
1396 replayWriter.setHeaderPrologue(frameIncludes);
1397 }
1398
1399 {
1400 std::stringstream protoStream;
1401 std::stringstream headerStream;
1402 std::stringstream bodyStream;
1403
1404 protoStream << "void " << FmtSetupFunction(kNoPartId, context->id());
1405 std::string proto = protoStream.str();
1406
1407 WriteCppReplayFunctionWithParts(context->id(), ReplayFunc::Setup, replayWriter, frameIndex,
1408 binaryData, setupCalls, headerStream, bodyStream);
1409
1410 replayWriter.addPrivateFunction(proto, headerStream, bodyStream);
1411 }
1412
1413 replayWriter.saveFrame();
1414 }
1415
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)1416 void WriteShareGroupCppSetupReplay(ReplayWriter &replayWriter,
1417 bool compression,
1418 const std::string &outDir,
1419 const std::string &captureLabel,
1420 uint32_t frameIndex,
1421 uint32_t frameCount,
1422 const std::vector<CallCapture> &setupCalls,
1423 ResourceTracker *resourceTracker,
1424 std::vector<uint8_t> *binaryData,
1425 bool serializeStateEnabled,
1426 gl::ContextID windowSurfaceContextID)
1427 {
1428 {
1429 std::stringstream include;
1430
1431 include << "#include \"angle_trace_gl.h\"\n";
1432 include << "#include \"" << FmtCapturePrefix(windowSurfaceContextID, captureLabel)
1433 << ".h\"\n";
1434
1435 std::string includeString = include.str();
1436
1437 replayWriter.setSourcePrologue(includeString);
1438 }
1439
1440 {
1441 std::stringstream protoStream;
1442 std::stringstream headerStream;
1443 std::stringstream bodyStream;
1444
1445 protoStream << "void " << FmtSetupFunction(kNoPartId, kSharedContextId);
1446 std::string proto = protoStream.str();
1447
1448 WriteCppReplayFunctionWithParts(kSharedContextId, ReplayFunc::Setup, replayWriter,
1449 frameIndex, binaryData, setupCalls, headerStream,
1450 bodyStream);
1451
1452 replayWriter.addPrivateFunction(proto, headerStream, bodyStream);
1453 }
1454
1455 {
1456 std::stringstream filenameStream;
1457 filenameStream << outDir << FmtCapturePrefix(kSharedContextId, captureLabel);
1458
1459 std::string filenamePattern = filenameStream.str();
1460
1461 replayWriter.setFilenamePattern(filenamePattern);
1462 }
1463
1464 replayWriter.saveSetupFile();
1465 }
1466
GetAttachedProgramSources(const gl::Program * program)1467 ProgramSources GetAttachedProgramSources(const gl::Program *program)
1468 {
1469 ProgramSources sources;
1470 for (gl::ShaderType shaderType : gl::AllShaderTypes())
1471 {
1472 const gl::Shader *shader = program->getAttachedShader(shaderType);
1473 if (shader)
1474 {
1475 sources[shaderType] = shader->getSourceString();
1476 }
1477 }
1478 return sources;
1479 }
1480
1481 template <typename IDType>
CaptureUpdateResourceIDs(const CallCapture & call,const ParamCapture & param,ResourceTracker * resourceTracker,std::vector<CallCapture> * callsOut)1482 void CaptureUpdateResourceIDs(const CallCapture &call,
1483 const ParamCapture ¶m,
1484 ResourceTracker *resourceTracker,
1485 std::vector<CallCapture> *callsOut)
1486 {
1487 GLsizei n = call.params.getParamFlexName("n", "count", ParamType::TGLsizei, 0).value.GLsizeiVal;
1488 ASSERT(param.data.size() == 1);
1489 ResourceIDType resourceIDType = GetResourceIDTypeFromParamType(param.type);
1490 ASSERT(resourceIDType != ResourceIDType::InvalidEnum &&
1491 resourceIDType != ResourceIDType::ShaderProgram);
1492 const char *resourceName = GetResourceIDTypeName(resourceIDType);
1493
1494 std::stringstream updateFuncNameStr;
1495 updateFuncNameStr << "Update" << resourceName << "ID";
1496 std::string updateFuncName = updateFuncNameStr.str();
1497
1498 const IDType *returnedIDs = reinterpret_cast<const IDType *>(param.data[0].data());
1499
1500 ResourceSet &startingSet =
1501 resourceTracker->getTrackedResource(resourceIDType).getStartingResources();
1502
1503 for (GLsizei idIndex = 0; idIndex < n; ++idIndex)
1504 {
1505 IDType id = returnedIDs[idIndex];
1506 GLsizei readBufferOffset = idIndex * sizeof(gl::RenderbufferID);
1507 ParamBuffer params;
1508 params.addValueParam("id", ParamType::TGLuint, id.value);
1509 params.addValueParam("readBufferOffset", ParamType::TGLsizei, readBufferOffset);
1510 callsOut->emplace_back(updateFuncName, std::move(params));
1511
1512 // Add only if not in starting resources.
1513 if (startingSet.find(id.value) == startingSet.end())
1514 {
1515 resourceTracker->getTrackedResource(resourceIDType).getNewResources().insert(id.value);
1516 }
1517 }
1518 }
1519
CaptureUpdateUniformLocations(const gl::Program * program,std::vector<CallCapture> * callsOut)1520 void CaptureUpdateUniformLocations(const gl::Program *program, std::vector<CallCapture> *callsOut)
1521 {
1522 const std::vector<gl::LinkedUniform> &uniforms = program->getState().getUniforms();
1523 const std::vector<gl::VariableLocation> &locations = program->getUniformLocations();
1524
1525 for (GLint location = 0; location < static_cast<GLint>(locations.size()); ++location)
1526 {
1527 const gl::VariableLocation &locationVar = locations[location];
1528
1529 // This handles the case where the application calls glBindUniformLocationCHROMIUM
1530 // on an unused uniform. We must still store a -1 into gUniformLocations in case the
1531 // application attempts to call a glUniform* call. To do this we'll pass in a blank name to
1532 // force glGetUniformLocation to return -1.
1533 std::string name;
1534 int count = 1;
1535 ParamBuffer params;
1536 params.addValueParam("program", ParamType::TGLuint, program->id().value);
1537
1538 if (locationVar.index >= uniforms.size())
1539 {
1540 name = "";
1541 }
1542 else
1543 {
1544 const gl::LinkedUniform &uniform = uniforms[locationVar.index];
1545
1546 name = uniform.name;
1547
1548 if (uniform.isArray())
1549 {
1550 if (locationVar.arrayIndex > 0)
1551 {
1552 // Non-sequential array uniform locations are not currently handled.
1553 // In practice array locations shouldn't ever be non-sequential.
1554 ASSERT(uniform.location == -1 ||
1555 location == uniform.location + static_cast<int>(locationVar.arrayIndex));
1556 continue;
1557 }
1558
1559 if (uniform.isArrayOfArrays())
1560 {
1561 UNIMPLEMENTED();
1562 }
1563
1564 name = gl::StripLastArrayIndex(name);
1565 count = uniform.arraySizes[0];
1566 }
1567 }
1568
1569 ParamCapture nameParam("name", ParamType::TGLcharConstPointer);
1570 CaptureString(name.c_str(), &nameParam);
1571 params.addParam(std::move(nameParam));
1572 params.addValueParam("location", ParamType::TGLint, location);
1573 params.addValueParam("count", ParamType::TGLint, static_cast<GLint>(count));
1574 callsOut->emplace_back("UpdateUniformLocation", std::move(params));
1575 }
1576 }
1577
CaptureValidateSerializedState(const gl::Context * context,std::vector<CallCapture> * callsOut)1578 void CaptureValidateSerializedState(const gl::Context *context, std::vector<CallCapture> *callsOut)
1579 {
1580 INFO() << "Capturing validation checkpoint at position " << callsOut->size();
1581
1582 context->finishImmutable();
1583
1584 std::string serializedState;
1585 angle::Result result = angle::SerializeContextToString(context, &serializedState);
1586 if (result != angle::Result::Continue)
1587 {
1588 ERR() << "Internal error serializing context state.";
1589 return;
1590 }
1591 ParamCapture serializedStateParam("serializedState", ParamType::TGLcharConstPointer);
1592 CaptureString(serializedState.c_str(), &serializedStateParam);
1593
1594 ParamBuffer params;
1595 params.addParam(std::move(serializedStateParam));
1596
1597 callsOut->emplace_back("VALIDATE_CHECKPOINT", std::move(params));
1598 }
1599
CaptureUpdateUniformBlockIndexes(const gl::Program * program,std::vector<CallCapture> * callsOut)1600 void CaptureUpdateUniformBlockIndexes(const gl::Program *program,
1601 std::vector<CallCapture> *callsOut)
1602 {
1603 const std::vector<gl::InterfaceBlock> &uniformBlocks = program->getState().getUniformBlocks();
1604
1605 for (GLuint index = 0; index < uniformBlocks.size(); ++index)
1606 {
1607 ParamBuffer params;
1608
1609 std::string name;
1610 params.addValueParam("program", ParamType::TShaderProgramID, program->id());
1611
1612 ParamCapture nameParam("name", ParamType::TGLcharConstPointer);
1613 CaptureString(uniformBlocks[index].name.c_str(), &nameParam);
1614 params.addParam(std::move(nameParam));
1615
1616 params.addValueParam("index", ParamType::TGLuint, index);
1617 callsOut->emplace_back("UpdateUniformBlockIndex", std::move(params));
1618 }
1619 }
1620
CaptureDeleteUniformLocations(gl::ShaderProgramID program,std::vector<CallCapture> * callsOut)1621 void CaptureDeleteUniformLocations(gl::ShaderProgramID program, std::vector<CallCapture> *callsOut)
1622 {
1623 ParamBuffer params;
1624 params.addValueParam("program", ParamType::TShaderProgramID, program);
1625 callsOut->emplace_back("DeleteUniformLocations", std::move(params));
1626 }
1627
MaybeCaptureUpdateResourceIDs(ResourceTracker * resourceTracker,std::vector<CallCapture> * callsOut)1628 void MaybeCaptureUpdateResourceIDs(ResourceTracker *resourceTracker,
1629 std::vector<CallCapture> *callsOut)
1630 {
1631 const CallCapture &call = callsOut->back();
1632
1633 switch (call.entryPoint)
1634 {
1635 case EntryPoint::GLGenBuffers:
1636 {
1637 const ParamCapture &buffers =
1638 call.params.getParam("buffersPacked", ParamType::TBufferIDPointer, 1);
1639 CaptureUpdateResourceIDs<gl::BufferID>(call, buffers, resourceTracker, callsOut);
1640 break;
1641 }
1642
1643 case EntryPoint::GLGenFencesNV:
1644 {
1645 const ParamCapture &fences =
1646 call.params.getParam("fencesPacked", ParamType::TFenceNVIDPointer, 1);
1647 CaptureUpdateResourceIDs<gl::FenceNVID>(call, fences, resourceTracker, callsOut);
1648 break;
1649 }
1650
1651 case EntryPoint::GLGenFramebuffers:
1652 case EntryPoint::GLGenFramebuffersOES:
1653 {
1654 const ParamCapture &framebuffers =
1655 call.params.getParam("framebuffersPacked", ParamType::TFramebufferIDPointer, 1);
1656 CaptureUpdateResourceIDs<gl::FramebufferID>(call, framebuffers, resourceTracker,
1657 callsOut);
1658 break;
1659 }
1660
1661 case EntryPoint::GLGenProgramPipelines:
1662 {
1663 const ParamCapture &pipelines =
1664 call.params.getParam("pipelinesPacked", ParamType::TProgramPipelineIDPointer, 1);
1665 CaptureUpdateResourceIDs<gl::ProgramPipelineID>(call, pipelines, resourceTracker,
1666 callsOut);
1667 break;
1668 }
1669
1670 case EntryPoint::GLGenQueries:
1671 case EntryPoint::GLGenQueriesEXT:
1672 {
1673 const ParamCapture &queries =
1674 call.params.getParam("idsPacked", ParamType::TQueryIDPointer, 1);
1675 CaptureUpdateResourceIDs<gl::QueryID>(call, queries, resourceTracker, callsOut);
1676 break;
1677 }
1678
1679 case EntryPoint::GLGenRenderbuffers:
1680 case EntryPoint::GLGenRenderbuffersOES:
1681 {
1682 const ParamCapture &renderbuffers =
1683 call.params.getParam("renderbuffersPacked", ParamType::TRenderbufferIDPointer, 1);
1684 CaptureUpdateResourceIDs<gl::RenderbufferID>(call, renderbuffers, resourceTracker,
1685 callsOut);
1686 break;
1687 }
1688
1689 case EntryPoint::GLGenSamplers:
1690 {
1691 const ParamCapture &samplers =
1692 call.params.getParam("samplersPacked", ParamType::TSamplerIDPointer, 1);
1693 CaptureUpdateResourceIDs<gl::SamplerID>(call, samplers, resourceTracker, callsOut);
1694 break;
1695 }
1696
1697 case EntryPoint::GLGenSemaphoresEXT:
1698 {
1699 const ParamCapture &semaphores =
1700 call.params.getParam("semaphoresPacked", ParamType::TSemaphoreIDPointer, 1);
1701 CaptureUpdateResourceIDs<gl::SemaphoreID>(call, semaphores, resourceTracker, callsOut);
1702 break;
1703 }
1704
1705 case EntryPoint::GLGenTextures:
1706 {
1707 const ParamCapture &textures =
1708 call.params.getParam("texturesPacked", ParamType::TTextureIDPointer, 1);
1709 CaptureUpdateResourceIDs<gl::TextureID>(call, textures, resourceTracker, callsOut);
1710 break;
1711 }
1712
1713 case EntryPoint::GLGenTransformFeedbacks:
1714 {
1715 const ParamCapture &xfbs =
1716 call.params.getParam("idsPacked", ParamType::TTransformFeedbackIDPointer, 1);
1717 CaptureUpdateResourceIDs<gl::TransformFeedbackID>(call, xfbs, resourceTracker,
1718 callsOut);
1719 break;
1720 }
1721
1722 case EntryPoint::GLGenVertexArrays:
1723 case EntryPoint::GLGenVertexArraysOES:
1724 {
1725 const ParamCapture &vertexArrays =
1726 call.params.getParam("arraysPacked", ParamType::TVertexArrayIDPointer, 1);
1727 CaptureUpdateResourceIDs<gl::VertexArrayID>(call, vertexArrays, resourceTracker,
1728 callsOut);
1729 break;
1730 }
1731
1732 case EntryPoint::GLCreateMemoryObjectsEXT:
1733 {
1734 const ParamCapture &memoryObjects =
1735 call.params.getParam("memoryObjectsPacked", ParamType::TMemoryObjectIDPointer, 1);
1736 CaptureUpdateResourceIDs<gl::MemoryObjectID>(call, memoryObjects, resourceTracker,
1737 callsOut);
1738 break;
1739 }
1740
1741 default:
1742 break;
1743 }
1744 }
1745
CaptureUpdateCurrentProgram(const CallCapture & call,int programParamPos,std::vector<CallCapture> * callsOut)1746 void CaptureUpdateCurrentProgram(const CallCapture &call,
1747 int programParamPos,
1748 std::vector<CallCapture> *callsOut)
1749 {
1750 const ParamCapture ¶m =
1751 call.params.getParam("programPacked", ParamType::TShaderProgramID, programParamPos);
1752 gl::ShaderProgramID programID = param.value.ShaderProgramIDVal;
1753
1754 ParamBuffer paramBuffer;
1755 paramBuffer.addValueParam("program", ParamType::TGLuint, programID.value);
1756
1757 callsOut->emplace_back("UpdateCurrentProgram", std::move(paramBuffer));
1758 }
1759
IsDefaultCurrentValue(const gl::VertexAttribCurrentValueData & currentValue)1760 bool IsDefaultCurrentValue(const gl::VertexAttribCurrentValueData ¤tValue)
1761 {
1762 if (currentValue.Type != gl::VertexAttribType::Float)
1763 return false;
1764
1765 return currentValue.Values.FloatValues[0] == 0.0f &&
1766 currentValue.Values.FloatValues[1] == 0.0f &&
1767 currentValue.Values.FloatValues[2] == 0.0f && currentValue.Values.FloatValues[3] == 1.0f;
1768 }
1769
IsQueryActive(const gl::State & glState,gl::QueryID & queryID)1770 bool IsQueryActive(const gl::State &glState, gl::QueryID &queryID)
1771 {
1772 const gl::ActiveQueryMap &activeQueries = glState.getActiveQueriesForCapture();
1773 for (const auto &activeQueryIter : activeQueries)
1774 {
1775 const gl::Query *activeQuery = activeQueryIter.get();
1776 if (activeQuery && activeQuery->id() == queryID)
1777 {
1778 return true;
1779 }
1780 }
1781
1782 return false;
1783 }
1784
IsTextureUpdate(CallCapture & call)1785 bool IsTextureUpdate(CallCapture &call)
1786 {
1787 switch (call.entryPoint)
1788 {
1789 case EntryPoint::GLCompressedCopyTextureCHROMIUM:
1790 case EntryPoint::GLCompressedTexImage1D:
1791 case EntryPoint::GLCompressedTexImage2D:
1792 case EntryPoint::GLCompressedTexImage2DRobustANGLE:
1793 case EntryPoint::GLCompressedTexImage3D:
1794 case EntryPoint::GLCompressedTexImage3DOES:
1795 case EntryPoint::GLCompressedTexImage3DRobustANGLE:
1796 case EntryPoint::GLCompressedTexSubImage1D:
1797 case EntryPoint::GLCompressedTexSubImage2D:
1798 case EntryPoint::GLCompressedTexSubImage2DRobustANGLE:
1799 case EntryPoint::GLCompressedTexSubImage3D:
1800 case EntryPoint::GLCompressedTexSubImage3DOES:
1801 case EntryPoint::GLCompressedTexSubImage3DRobustANGLE:
1802 case EntryPoint::GLCompressedTextureSubImage1D:
1803 case EntryPoint::GLCompressedTextureSubImage2D:
1804 case EntryPoint::GLCompressedTextureSubImage3D:
1805 case EntryPoint::GLCopyTexImage1D:
1806 case EntryPoint::GLCopyTexImage2D:
1807 case EntryPoint::GLCopyTexSubImage1D:
1808 case EntryPoint::GLCopyTexSubImage2D:
1809 case EntryPoint::GLCopyTexSubImage3D:
1810 case EntryPoint::GLCopyTexSubImage3DOES:
1811 case EntryPoint::GLCopyTexture3DANGLE:
1812 case EntryPoint::GLCopyTextureCHROMIUM:
1813 case EntryPoint::GLCopyTextureSubImage1D:
1814 case EntryPoint::GLCopyTextureSubImage2D:
1815 case EntryPoint::GLCopyTextureSubImage3D:
1816 case EntryPoint::GLTexImage1D:
1817 case EntryPoint::GLTexImage2D:
1818 case EntryPoint::GLTexImage2DExternalANGLE:
1819 case EntryPoint::GLTexImage2DMultisample:
1820 case EntryPoint::GLTexImage2DRobustANGLE:
1821 case EntryPoint::GLTexImage3D:
1822 case EntryPoint::GLTexImage3DMultisample:
1823 case EntryPoint::GLTexImage3DOES:
1824 case EntryPoint::GLTexImage3DRobustANGLE:
1825 case EntryPoint::GLTexSubImage1D:
1826 case EntryPoint::GLTexSubImage2D:
1827 case EntryPoint::GLTexSubImage2DRobustANGLE:
1828 case EntryPoint::GLTexSubImage3D:
1829 case EntryPoint::GLTexSubImage3DOES:
1830 case EntryPoint::GLTexSubImage3DRobustANGLE:
1831 case EntryPoint::GLTextureSubImage1D:
1832 case EntryPoint::GLTextureSubImage2D:
1833 case EntryPoint::GLTextureSubImage3D:
1834 case EntryPoint::GLCopyImageSubData:
1835 case EntryPoint::GLCopyImageSubDataEXT:
1836 case EntryPoint::GLCopyImageSubDataOES:
1837 return true;
1838 default:
1839 return false;
1840 }
1841 }
1842
Capture(std::vector<CallCapture> * setupCalls,CallCapture && call)1843 void Capture(std::vector<CallCapture> *setupCalls, CallCapture &&call)
1844 {
1845 setupCalls->emplace_back(std::move(call));
1846 }
1847
CaptureFramebufferAttachment(std::vector<CallCapture> * setupCalls,const gl::State & replayState,const FramebufferCaptureFuncs & framebufferFuncs,const gl::FramebufferAttachment & attachment)1848 void CaptureFramebufferAttachment(std::vector<CallCapture> *setupCalls,
1849 const gl::State &replayState,
1850 const FramebufferCaptureFuncs &framebufferFuncs,
1851 const gl::FramebufferAttachment &attachment)
1852 {
1853 GLuint resourceID = attachment.getResource()->getId();
1854
1855 if (attachment.type() == GL_TEXTURE)
1856 {
1857 gl::ImageIndex index = attachment.getTextureImageIndex();
1858
1859 if (index.usesTex3D())
1860 {
1861 Capture(setupCalls, CaptureFramebufferTextureLayer(
1862 replayState, true, GL_FRAMEBUFFER, attachment.getBinding(),
1863 {resourceID}, index.getLevelIndex(), index.getLayerIndex()));
1864 }
1865 else
1866 {
1867 Capture(setupCalls, framebufferFuncs.framebufferTexture2D(
1868 replayState, true, GL_FRAMEBUFFER, attachment.getBinding(),
1869 index.getTarget(), {resourceID}, index.getLevelIndex()));
1870 }
1871 }
1872 else
1873 {
1874 ASSERT(attachment.type() == GL_RENDERBUFFER);
1875 Capture(setupCalls, framebufferFuncs.framebufferRenderbuffer(
1876 replayState, true, GL_FRAMEBUFFER, attachment.getBinding(),
1877 GL_RENDERBUFFER, {resourceID}));
1878 }
1879 }
1880
CaptureUpdateUniformValues(const gl::State & replayState,const gl::Context * context,gl::Program * program,std::vector<CallCapture> * callsOut)1881 void CaptureUpdateUniformValues(const gl::State &replayState,
1882 const gl::Context *context,
1883 gl::Program *program,
1884 std::vector<CallCapture> *callsOut)
1885 {
1886 if (!program->isLinked())
1887 {
1888 // We can't populate uniforms if the program hasn't been linked
1889 return;
1890 }
1891
1892 // We need to bind the program and update its uniforms
1893 if (!replayState.getProgram() || replayState.getProgram()->id() != program->id())
1894 {
1895 Capture(callsOut, CaptureUseProgram(replayState, true, program->id()));
1896 CaptureUpdateCurrentProgram(callsOut->back(), 0, callsOut);
1897 }
1898
1899 const std::vector<gl::LinkedUniform> &uniforms = program->getState().getUniforms();
1900
1901 for (const gl::LinkedUniform &uniform : uniforms)
1902 {
1903 std::string uniformName = uniform.name;
1904
1905 int uniformCount = 1;
1906 if (uniform.isArray())
1907 {
1908 if (uniform.isArrayOfArrays())
1909 {
1910 UNIMPLEMENTED();
1911 continue;
1912 }
1913
1914 uniformCount = uniform.arraySizes[0];
1915 uniformName = gl::StripLastArrayIndex(uniformName);
1916 }
1917
1918 gl::UniformLocation uniformLoc = program->getUniformLocation(uniformName);
1919 const gl::UniformTypeInfo *typeInfo = uniform.typeInfo;
1920 int componentCount = typeInfo->componentCount;
1921 int uniformSize = uniformCount * componentCount;
1922
1923 // For arrayed uniforms, we'll need to increment a read location
1924 gl::UniformLocation readLoc = uniformLoc;
1925
1926 // If the uniform is unused, just continue
1927 if (readLoc.value == -1)
1928 {
1929 continue;
1930 }
1931
1932 // Image uniforms are special and cannot be set this way
1933 if (typeInfo->isImageType)
1934 {
1935 continue;
1936 }
1937
1938 // Samplers should be populated with GL_INT, regardless of return type
1939 if (typeInfo->isSampler)
1940 {
1941 std::vector<GLint> uniformBuffer(uniformSize);
1942 for (int index = 0; index < uniformCount; index++, readLoc.value++)
1943 {
1944 program->getUniformiv(context, readLoc,
1945 uniformBuffer.data() + index * componentCount);
1946 }
1947
1948 Capture(callsOut, CaptureUniform1iv(replayState, true, uniformLoc, uniformCount,
1949 uniformBuffer.data()));
1950
1951 continue;
1952 }
1953
1954 switch (typeInfo->componentType)
1955 {
1956 case GL_FLOAT:
1957 {
1958 std::vector<GLfloat> uniformBuffer(uniformSize);
1959 for (int index = 0; index < uniformCount; index++, readLoc.value++)
1960 {
1961 program->getUniformfv(context, readLoc,
1962 uniformBuffer.data() + index * componentCount);
1963 }
1964 switch (typeInfo->type)
1965 {
1966 // Note: All matrix uniforms are populated without transpose
1967 case GL_FLOAT_MAT4x3:
1968 Capture(callsOut, CaptureUniformMatrix4x3fv(replayState, true, uniformLoc,
1969 uniformCount, false,
1970 uniformBuffer.data()));
1971 break;
1972 case GL_FLOAT_MAT4x2:
1973 Capture(callsOut, CaptureUniformMatrix4x2fv(replayState, true, uniformLoc,
1974 uniformCount, false,
1975 uniformBuffer.data()));
1976 break;
1977 case GL_FLOAT_MAT4:
1978 Capture(callsOut,
1979 CaptureUniformMatrix4fv(replayState, true, uniformLoc, uniformCount,
1980 false, uniformBuffer.data()));
1981 break;
1982 case GL_FLOAT_MAT3x4:
1983 Capture(callsOut, CaptureUniformMatrix3x4fv(replayState, true, uniformLoc,
1984 uniformCount, false,
1985 uniformBuffer.data()));
1986 break;
1987 case GL_FLOAT_MAT3x2:
1988 Capture(callsOut, CaptureUniformMatrix3x2fv(replayState, true, uniformLoc,
1989 uniformCount, false,
1990 uniformBuffer.data()));
1991 break;
1992 case GL_FLOAT_MAT3:
1993 Capture(callsOut,
1994 CaptureUniformMatrix3fv(replayState, true, uniformLoc, uniformCount,
1995 false, uniformBuffer.data()));
1996 break;
1997 case GL_FLOAT_MAT2x4:
1998 Capture(callsOut, CaptureUniformMatrix2x4fv(replayState, true, uniformLoc,
1999 uniformCount, false,
2000 uniformBuffer.data()));
2001 break;
2002 case GL_FLOAT_MAT2x3:
2003 Capture(callsOut, CaptureUniformMatrix2x3fv(replayState, true, uniformLoc,
2004 uniformCount, false,
2005 uniformBuffer.data()));
2006 break;
2007 case GL_FLOAT_MAT2:
2008 Capture(callsOut,
2009 CaptureUniformMatrix2fv(replayState, true, uniformLoc, uniformCount,
2010 false, uniformBuffer.data()));
2011 break;
2012 case GL_FLOAT_VEC4:
2013 Capture(callsOut, CaptureUniform4fv(replayState, true, uniformLoc,
2014 uniformCount, uniformBuffer.data()));
2015 break;
2016 case GL_FLOAT_VEC3:
2017 Capture(callsOut, CaptureUniform3fv(replayState, true, uniformLoc,
2018 uniformCount, uniformBuffer.data()));
2019 break;
2020 case GL_FLOAT_VEC2:
2021 Capture(callsOut, CaptureUniform2fv(replayState, true, uniformLoc,
2022 uniformCount, uniformBuffer.data()));
2023 break;
2024 case GL_FLOAT:
2025 Capture(callsOut, CaptureUniform1fv(replayState, true, uniformLoc,
2026 uniformCount, uniformBuffer.data()));
2027 break;
2028 default:
2029 UNIMPLEMENTED();
2030 break;
2031 }
2032 break;
2033 }
2034 case GL_INT:
2035 {
2036 std::vector<GLint> uniformBuffer(uniformSize);
2037 for (int index = 0; index < uniformCount; index++, readLoc.value++)
2038 {
2039 program->getUniformiv(context, readLoc,
2040 uniformBuffer.data() + index * componentCount);
2041 }
2042 switch (componentCount)
2043 {
2044 case 4:
2045 Capture(callsOut, CaptureUniform4iv(replayState, true, uniformLoc,
2046 uniformCount, uniformBuffer.data()));
2047 break;
2048 case 3:
2049 Capture(callsOut, CaptureUniform3iv(replayState, true, uniformLoc,
2050 uniformCount, uniformBuffer.data()));
2051 break;
2052 case 2:
2053 Capture(callsOut, CaptureUniform2iv(replayState, true, uniformLoc,
2054 uniformCount, uniformBuffer.data()));
2055 break;
2056 case 1:
2057 Capture(callsOut, CaptureUniform1iv(replayState, true, uniformLoc,
2058 uniformCount, uniformBuffer.data()));
2059 break;
2060 default:
2061 UNIMPLEMENTED();
2062 break;
2063 }
2064 break;
2065 }
2066 case GL_BOOL:
2067 case GL_UNSIGNED_INT:
2068 {
2069 std::vector<GLuint> uniformBuffer(uniformSize);
2070 for (int index = 0; index < uniformCount; index++, readLoc.value++)
2071 {
2072 program->getUniformuiv(context, readLoc,
2073 uniformBuffer.data() + index * componentCount);
2074 }
2075 switch (componentCount)
2076 {
2077 case 4:
2078 Capture(callsOut, CaptureUniform4uiv(replayState, true, uniformLoc,
2079 uniformCount, uniformBuffer.data()));
2080 break;
2081 case 3:
2082 Capture(callsOut, CaptureUniform3uiv(replayState, true, uniformLoc,
2083 uniformCount, uniformBuffer.data()));
2084 break;
2085 case 2:
2086 Capture(callsOut, CaptureUniform2uiv(replayState, true, uniformLoc,
2087 uniformCount, uniformBuffer.data()));
2088 break;
2089 case 1:
2090 Capture(callsOut, CaptureUniform1uiv(replayState, true, uniformLoc,
2091 uniformCount, uniformBuffer.data()));
2092 break;
2093 default:
2094 UNIMPLEMENTED();
2095 break;
2096 }
2097 break;
2098 }
2099 default:
2100 UNIMPLEMENTED();
2101 break;
2102 }
2103 }
2104 }
2105
CaptureVertexPointerES1(std::vector<CallCapture> * setupCalls,gl::State * replayState,GLuint attribIndex,const gl::VertexAttribute & attrib,const gl::VertexBinding & binding)2106 void CaptureVertexPointerES1(std::vector<CallCapture> *setupCalls,
2107 gl::State *replayState,
2108 GLuint attribIndex,
2109 const gl::VertexAttribute &attrib,
2110 const gl::VertexBinding &binding)
2111 {
2112 switch (gl::GLES1Renderer::VertexArrayType(attribIndex))
2113 {
2114 case gl::ClientVertexArrayType::Vertex:
2115 Capture(setupCalls,
2116 CaptureVertexPointer(*replayState, true, attrib.format->channelCount,
2117 attrib.format->vertexAttribType, binding.getStride(),
2118 attrib.pointer));
2119 break;
2120 case gl::ClientVertexArrayType::Normal:
2121 Capture(setupCalls,
2122 CaptureNormalPointer(*replayState, true, attrib.format->vertexAttribType,
2123 binding.getStride(), attrib.pointer));
2124 break;
2125 case gl::ClientVertexArrayType::Color:
2126 Capture(setupCalls, CaptureColorPointer(*replayState, true, attrib.format->channelCount,
2127 attrib.format->vertexAttribType,
2128 binding.getStride(), attrib.pointer));
2129 break;
2130 case gl::ClientVertexArrayType::PointSize:
2131 Capture(setupCalls,
2132 CapturePointSizePointerOES(*replayState, true, attrib.format->vertexAttribType,
2133 binding.getStride(), attrib.pointer));
2134 break;
2135 case gl::ClientVertexArrayType::TextureCoord:
2136 Capture(setupCalls,
2137 CaptureTexCoordPointer(*replayState, true, attrib.format->channelCount,
2138 attrib.format->vertexAttribType, binding.getStride(),
2139 attrib.pointer));
2140 break;
2141 default:
2142 UNREACHABLE();
2143 }
2144 }
2145
VertexBindingMatchesAttribStride(const gl::VertexAttribute & attrib,const gl::VertexBinding & binding)2146 bool VertexBindingMatchesAttribStride(const gl::VertexAttribute &attrib,
2147 const gl::VertexBinding &binding)
2148 {
2149 if (attrib.vertexAttribArrayStride == 0 &&
2150 binding.getStride() == ComputeVertexAttributeTypeSize(attrib))
2151 {
2152 return true;
2153 }
2154
2155 return attrib.vertexAttribArrayStride == binding.getStride();
2156 }
2157
CaptureVertexArrayState(std::vector<CallCapture> * setupCalls,const gl::Context * context,const gl::VertexArray * vertexArray,gl::State * replayState)2158 void CaptureVertexArrayState(std::vector<CallCapture> *setupCalls,
2159 const gl::Context *context,
2160 const gl::VertexArray *vertexArray,
2161 gl::State *replayState)
2162 {
2163 const std::vector<gl::VertexAttribute> &vertexAttribs = vertexArray->getVertexAttributes();
2164 const std::vector<gl::VertexBinding> &vertexBindings = vertexArray->getVertexBindings();
2165
2166 gl::AttributesMask vertexPointerBindings;
2167
2168 ASSERT(vertexAttribs.size() <= vertexBindings.size());
2169 for (GLuint attribIndex = 0; attribIndex < vertexAttribs.size(); ++attribIndex)
2170 {
2171 const gl::VertexAttribute defaultAttrib(attribIndex);
2172 const gl::VertexBinding defaultBinding;
2173
2174 const gl::VertexAttribute &attrib = vertexAttribs[attribIndex];
2175 const gl::VertexBinding &binding = vertexBindings[attrib.bindingIndex];
2176
2177 if (attrib.enabled != defaultAttrib.enabled)
2178 {
2179 if (context->isGLES1())
2180 {
2181 Capture(setupCalls,
2182 CaptureEnableClientState(*replayState, false,
2183 gl::GLES1Renderer::VertexArrayType(attribIndex)));
2184 }
2185 else
2186 {
2187 Capture(setupCalls,
2188 CaptureEnableVertexAttribArray(*replayState, false, attribIndex));
2189 }
2190 }
2191
2192 // Don't capture CaptureVertexAttribPointer calls when a non-default VAO is bound, the array
2193 // buffer is null and a non-null attrib pointer is used.
2194 bool skipInvalidAttrib = vertexArray->id().value != 0 &&
2195 binding.getBuffer().get() == nullptr && attrib.pointer != nullptr;
2196
2197 if (!skipInvalidAttrib &&
2198 (attrib.format != defaultAttrib.format || attrib.pointer != defaultAttrib.pointer ||
2199 binding.getStride() != defaultBinding.getStride() ||
2200 binding.getBuffer().get() != nullptr))
2201 {
2202 // Each attribute can pull from a separate buffer, so check the binding
2203 gl::Buffer *buffer = binding.getBuffer().get();
2204 if (buffer != replayState->getArrayBuffer())
2205 {
2206 replayState->setBufferBinding(context, gl::BufferBinding::Array, buffer);
2207
2208 gl::BufferID bufferID = {0};
2209 if (buffer)
2210 {
2211 bufferID = buffer->id();
2212 }
2213 Capture(setupCalls,
2214 CaptureBindBuffer(*replayState, true, gl::BufferBinding::Array, bufferID));
2215 }
2216
2217 // Establish the relationship between currently bound buffer and the VAO
2218 if (context->isGLES1())
2219 {
2220 // Track indexes that used ES1 calls
2221 vertexPointerBindings.set(attribIndex);
2222
2223 CaptureVertexPointerES1(setupCalls, replayState, attribIndex, attrib, binding);
2224 }
2225 else if (attrib.bindingIndex == attribIndex &&
2226 VertexBindingMatchesAttribStride(attrib, binding) &&
2227 (!buffer || binding.getOffset() == reinterpret_cast<GLintptr>(attrib.pointer)))
2228 {
2229 // Check if we can use strictly ES2 semantics, and track indexes that do.
2230 vertexPointerBindings.set(attribIndex);
2231
2232 if (attrib.format->isPureInt())
2233 {
2234 Capture(setupCalls, CaptureVertexAttribIPointer(*replayState, true, attribIndex,
2235 attrib.format->channelCount,
2236 attrib.format->vertexAttribType,
2237 attrib.vertexAttribArrayStride,
2238 attrib.pointer));
2239 }
2240 else
2241 {
2242 Capture(setupCalls,
2243 CaptureVertexAttribPointer(
2244 *replayState, true, attribIndex, attrib.format->channelCount,
2245 attrib.format->vertexAttribType, attrib.format->isNorm(),
2246 attrib.vertexAttribArrayStride, attrib.pointer));
2247 }
2248
2249 if (binding.getDivisor() != 0)
2250 {
2251 Capture(setupCalls, CaptureVertexAttribDivisor(*replayState, true, attribIndex,
2252 binding.getDivisor()));
2253 }
2254 }
2255 else
2256 {
2257 ASSERT(context->getClientVersion() >= gl::ES_3_1);
2258
2259 if (attrib.format->isPureInt())
2260 {
2261 Capture(setupCalls, CaptureVertexAttribIFormat(*replayState, true, attribIndex,
2262 attrib.format->channelCount,
2263 attrib.format->vertexAttribType,
2264 attrib.relativeOffset));
2265 }
2266 else
2267 {
2268 Capture(setupCalls, CaptureVertexAttribFormat(*replayState, true, attribIndex,
2269 attrib.format->channelCount,
2270 attrib.format->vertexAttribType,
2271 attrib.format->isNorm(),
2272 attrib.relativeOffset));
2273 }
2274
2275 Capture(setupCalls, CaptureVertexAttribBinding(*replayState, true, attribIndex,
2276 attrib.bindingIndex));
2277 }
2278 }
2279 }
2280
2281 // The loop below expects attribs and bindings to have equal counts
2282 static_assert(gl::MAX_VERTEX_ATTRIBS == gl::MAX_VERTEX_ATTRIB_BINDINGS,
2283 "Max vertex attribs and bindings count mismatch");
2284
2285 // Loop through binding indices that weren't used by VertexAttribPointer
2286 for (size_t bindingIndex : vertexPointerBindings.flip())
2287 {
2288 const gl::VertexBinding &binding = vertexBindings[bindingIndex];
2289
2290 if (binding.getBuffer().id().value != 0)
2291 {
2292 Capture(setupCalls,
2293 CaptureBindVertexBuffer(*replayState, true, static_cast<GLuint>(bindingIndex),
2294 binding.getBuffer().id(), binding.getOffset(),
2295 binding.getStride()));
2296 }
2297
2298 if (binding.getDivisor() != 0)
2299 {
2300 Capture(setupCalls, CaptureVertexBindingDivisor(*replayState, true,
2301 static_cast<GLuint>(bindingIndex),
2302 binding.getDivisor()));
2303 }
2304 }
2305
2306 // The element array buffer is not per attribute, but per VAO
2307 gl::Buffer *elementArrayBuffer = vertexArray->getElementArrayBuffer();
2308 if (elementArrayBuffer)
2309 {
2310 Capture(setupCalls, CaptureBindBuffer(*replayState, true, gl::BufferBinding::ElementArray,
2311 elementArrayBuffer->id()));
2312 }
2313 }
2314
CaptureTextureStorage(std::vector<CallCapture> * setupCalls,gl::State * replayState,const gl::Texture * texture)2315 void CaptureTextureStorage(std::vector<CallCapture> *setupCalls,
2316 gl::State *replayState,
2317 const gl::Texture *texture)
2318 {
2319 // Use mip-level 0 for the base dimensions
2320 gl::ImageIndex imageIndex = gl::ImageIndex::MakeFromType(texture->getType(), 0);
2321 const gl::ImageDesc &desc = texture->getTextureState().getImageDesc(imageIndex);
2322
2323 switch (texture->getType())
2324 {
2325 case gl::TextureType::_2D:
2326 case gl::TextureType::CubeMap:
2327 {
2328 Capture(setupCalls, CaptureTexStorage2D(*replayState, true, texture->getType(),
2329 texture->getImmutableLevels(),
2330 desc.format.info->internalFormat,
2331 desc.size.width, desc.size.height));
2332 break;
2333 }
2334 case gl::TextureType::_3D:
2335 case gl::TextureType::_2DArray:
2336 case gl::TextureType::CubeMapArray:
2337 {
2338 Capture(setupCalls, CaptureTexStorage3D(
2339 *replayState, true, texture->getType(),
2340 texture->getImmutableLevels(), desc.format.info->internalFormat,
2341 desc.size.width, desc.size.height, desc.size.depth));
2342 break;
2343 }
2344 case gl::TextureType::Buffer:
2345 {
2346 // Do nothing. This will already be captured as a buffer.
2347 break;
2348 }
2349 default:
2350 UNIMPLEMENTED();
2351 break;
2352 }
2353 }
2354
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)2355 void CaptureTextureContents(std::vector<CallCapture> *setupCalls,
2356 gl::State *replayState,
2357 const gl::Texture *texture,
2358 const gl::ImageIndex &index,
2359 const gl::ImageDesc &desc,
2360 GLuint size,
2361 const void *data)
2362 {
2363 const gl::InternalFormat &format = *desc.format.info;
2364
2365 if (index.getType() == gl::TextureType::Buffer)
2366 {
2367 // Zero binding size indicates full buffer bound
2368 if (texture->getBuffer().getSize() == 0)
2369 {
2370 Capture(setupCalls,
2371 CaptureTexBufferEXT(*replayState, true, index.getType(), format.internalFormat,
2372 texture->getBuffer().get()->id()));
2373 }
2374 else
2375 {
2376 Capture(setupCalls, CaptureTexBufferRangeEXT(*replayState, true, index.getType(),
2377 format.internalFormat,
2378 texture->getBuffer().get()->id(),
2379 texture->getBuffer().getOffset(),
2380 texture->getBuffer().getSize()));
2381 }
2382
2383 // For buffers, we're done
2384 return;
2385 }
2386
2387 bool is3D =
2388 (index.getType() == gl::TextureType::_3D || index.getType() == gl::TextureType::_2DArray ||
2389 index.getType() == gl::TextureType::CubeMapArray);
2390
2391 if (format.compressed)
2392 {
2393 if (is3D)
2394 {
2395 if (texture->getImmutableFormat())
2396 {
2397 Capture(setupCalls,
2398 CaptureCompressedTexSubImage3D(
2399 *replayState, true, index.getTarget(), index.getLevelIndex(), 0, 0, 0,
2400 desc.size.width, desc.size.height, desc.size.depth,
2401 format.internalFormat, size, data));
2402 }
2403 else
2404 {
2405 Capture(setupCalls,
2406 CaptureCompressedTexImage3D(*replayState, true, index.getTarget(),
2407 index.getLevelIndex(), format.internalFormat,
2408 desc.size.width, desc.size.height,
2409 desc.size.depth, 0, size, data));
2410 }
2411 }
2412 else
2413 {
2414 if (texture->getImmutableFormat())
2415 {
2416 Capture(setupCalls,
2417 CaptureCompressedTexSubImage2D(
2418 *replayState, true, index.getTarget(), index.getLevelIndex(), 0, 0,
2419 desc.size.width, desc.size.height, format.internalFormat, size, data));
2420 }
2421 else
2422 {
2423 Capture(setupCalls, CaptureCompressedTexImage2D(
2424 *replayState, true, index.getTarget(),
2425 index.getLevelIndex(), format.internalFormat,
2426 desc.size.width, desc.size.height, 0, size, data));
2427 }
2428 }
2429 }
2430 else
2431 {
2432 if (is3D)
2433 {
2434 if (texture->getImmutableFormat())
2435 {
2436 Capture(setupCalls,
2437 CaptureTexSubImage3D(*replayState, true, index.getTarget(),
2438 index.getLevelIndex(), 0, 0, 0, desc.size.width,
2439 desc.size.height, desc.size.depth, format.format,
2440 format.type, data));
2441 }
2442 else
2443 {
2444 Capture(
2445 setupCalls,
2446 CaptureTexImage3D(*replayState, true, index.getTarget(), index.getLevelIndex(),
2447 format.internalFormat, desc.size.width, desc.size.height,
2448 desc.size.depth, 0, format.format, format.type, data));
2449 }
2450 }
2451 else
2452 {
2453 if (texture->getImmutableFormat())
2454 {
2455 Capture(setupCalls,
2456 CaptureTexSubImage2D(*replayState, true, index.getTarget(),
2457 index.getLevelIndex(), 0, 0, desc.size.width,
2458 desc.size.height, format.format, format.type, data));
2459 }
2460 else
2461 {
2462 Capture(setupCalls, CaptureTexImage2D(*replayState, true, index.getTarget(),
2463 index.getLevelIndex(), format.internalFormat,
2464 desc.size.width, desc.size.height, 0,
2465 format.format, format.type, data));
2466 }
2467 }
2468 }
2469 }
2470
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)2471 void GenerateLinkedProgram(const gl::Context *context,
2472 const gl::State &replayState,
2473 ResourceTracker *resourceTracker,
2474 std::vector<CallCapture> *setupCalls,
2475 gl::Program *program,
2476 gl::ShaderProgramID id,
2477 gl::ShaderProgramID tempIDStart,
2478 const ProgramSources &linkedSources)
2479 {
2480 // A map to store the gShaderProgram map lookup index of the temp shaders we attached below. We
2481 // need this map to retrieve the lookup index to pass to CaptureDetachShader calls at the end of
2482 // GenerateLinkedProgram.
2483 PackedEnumMap<gl::ShaderType, gl::ShaderProgramID> tempShaderIDTracker;
2484
2485 // Compile with last linked sources.
2486 for (gl::ShaderType shaderType : program->getExecutable().getLinkedShaderStages())
2487 {
2488 // Bump the max shader program id for each new tempIDStart we use to create, compile, and
2489 // attach the temp shader object.
2490 resourceTracker->onShaderProgramAccess(tempIDStart);
2491 // Store the tempIDStart in the tempShaderIDTracker to retrieve for CaptureDetachShader
2492 // calls later.
2493 tempShaderIDTracker[shaderType] = tempIDStart;
2494 const std::string &sourceString = linkedSources[shaderType];
2495 const char *sourcePointer = sourceString.c_str();
2496
2497 if (sourceString.empty())
2498 {
2499 // If we don't have source for this shader, that means it was populated by the app
2500 // using glProgramBinary. We need to look it up from our cached copy.
2501 const ProgramSources &cachedLinkedSources =
2502 context->getShareGroup()->getFrameCaptureShared()->getProgramSources(id);
2503
2504 const std::string &cachedSourceString = cachedLinkedSources[shaderType];
2505 sourcePointer = cachedSourceString.c_str();
2506 ASSERT(!cachedSourceString.empty());
2507 }
2508
2509 // Compile and attach the temporary shader. Then free it immediately.
2510 Capture(setupCalls, CaptureCreateShader(replayState, true, shaderType, tempIDStart.value));
2511 Capture(setupCalls,
2512 CaptureShaderSource(replayState, true, tempIDStart, 1, &sourcePointer, nullptr));
2513 Capture(setupCalls, CaptureCompileShader(replayState, true, tempIDStart));
2514 Capture(setupCalls, CaptureAttachShader(replayState, true, id, tempIDStart));
2515 // Increment tempIDStart to get a new gShaderProgram map index for the next linked stage
2516 // shader object. We can't reuse the same tempIDStart as we need to retrieve the index of
2517 // each attached shader object later to pass to CaptureDetachShader calls.
2518 tempIDStart.value += 1;
2519 }
2520
2521 // Gather XFB varyings
2522 std::vector<std::string> xfbVaryings;
2523 for (const gl::TransformFeedbackVarying &xfbVarying :
2524 program->getState().getLinkedTransformFeedbackVaryings())
2525 {
2526 xfbVaryings.push_back(xfbVarying.nameWithArrayIndex());
2527 }
2528
2529 if (!xfbVaryings.empty())
2530 {
2531 std::vector<const char *> varyingsStrings;
2532 for (const std::string &varyingString : xfbVaryings)
2533 {
2534 varyingsStrings.push_back(varyingString.data());
2535 }
2536
2537 GLenum xfbMode = program->getState().getTransformFeedbackBufferMode();
2538 Capture(setupCalls, CaptureTransformFeedbackVaryings(replayState, true, id,
2539 static_cast<GLint>(xfbVaryings.size()),
2540 varyingsStrings.data(), xfbMode));
2541 }
2542
2543 // Force the attributes to be bound the same way as in the existing program.
2544 // This can affect attributes that are optimized out in some implementations.
2545 for (const sh::ShaderVariable &attrib : program->getState().getProgramInputs())
2546 {
2547 if (gl::IsBuiltInName(attrib.name))
2548 {
2549 // Don't try to bind built-in attributes
2550 continue;
2551 }
2552
2553 // Separable programs may not have a VS, meaning it may not have attributes.
2554 if (program->getExecutable().hasLinkedShaderStage(gl::ShaderType::Vertex))
2555 {
2556 ASSERT(attrib.location != -1);
2557 Capture(setupCalls, CaptureBindAttribLocation(replayState, true, id,
2558 static_cast<GLuint>(attrib.location),
2559 attrib.name.c_str()));
2560 }
2561 }
2562
2563 if (program->isSeparable())
2564 {
2565 // MEC manually recreates separable programs, rather than attempting to recreate a call
2566 // to glCreateShaderProgramv(), so insert a call to mark it separable.
2567 Capture(setupCalls,
2568 CaptureProgramParameteri(replayState, true, id, GL_PROGRAM_SEPARABLE, GL_TRUE));
2569 }
2570
2571 Capture(setupCalls, CaptureLinkProgram(replayState, true, id));
2572 CaptureUpdateUniformLocations(program, setupCalls);
2573 CaptureUpdateUniformValues(replayState, context, program, setupCalls);
2574 CaptureUpdateUniformBlockIndexes(program, setupCalls);
2575
2576 // Capture uniform block bindings for each program
2577 for (unsigned int uniformBlockIndex = 0;
2578 uniformBlockIndex < program->getActiveUniformBlockCount(); uniformBlockIndex++)
2579 {
2580 GLuint blockBinding = program->getUniformBlockBinding(uniformBlockIndex);
2581 Capture(setupCalls, CaptureUniformBlockBinding(replayState, true, id, {uniformBlockIndex},
2582 blockBinding));
2583 }
2584
2585 // Add DetachShader call if that's what the app does, so that the
2586 // ResourceManagerBase::mHandleAllocator can release the ShaderProgramID handle assigned to the
2587 // shader object when glDeleteShader is called. This ensures the ShaderProgramID handles used in
2588 // SetupReplayContextShared() are consistent with the ShaderProgramID handles used by the app.
2589 for (gl::ShaderType shaderType : program->getExecutable().getLinkedShaderStages())
2590 {
2591 gl::Shader *attachedShader = program->getAttachedShader(shaderType);
2592 if (attachedShader == nullptr)
2593 {
2594 Capture(setupCalls,
2595 CaptureDetachShader(replayState, true, id, tempShaderIDTracker[shaderType]));
2596 }
2597 Capture(setupCalls,
2598 CaptureDeleteShader(replayState, true, tempShaderIDTracker[shaderType]));
2599 }
2600 }
2601
2602 // TODO(http://anglebug.com/4599): Improve reset/restore call generation
2603 // There are multiple ways to track reset calls for individual resources. For now, we are tracking
2604 // separate lists of instructions that mirror the calls created during mid-execution setup. Other
2605 // methods could involve passing the original CallCaptures to this function, or tracking the
2606 // indices of original setup calls.
CaptureBufferResetCalls(const gl::State & replayState,ResourceTracker * resourceTracker,gl::BufferID * id,const gl::Buffer * buffer)2607 void CaptureBufferResetCalls(const gl::State &replayState,
2608 ResourceTracker *resourceTracker,
2609 gl::BufferID *id,
2610 const gl::Buffer *buffer)
2611 {
2612 GLuint bufferID = (*id).value;
2613
2614 // Track this as a starting resource that may need to be restored.
2615 TrackedResource &trackedBuffers = resourceTracker->getTrackedResource(ResourceIDType::Buffer);
2616
2617 // Track calls to regenerate a given buffer
2618 ResourceCalls &bufferRegenCalls = trackedBuffers.getResourceRegenCalls();
2619 Capture(&bufferRegenCalls[bufferID], CaptureGenBuffers(replayState, true, 1, id));
2620 MaybeCaptureUpdateResourceIDs(resourceTracker, &bufferRegenCalls[bufferID]);
2621
2622 // Call glBufferStorageEXT when regenerating immutable buffers,
2623 // as we can't call glBufferData on restore.
2624 if (buffer->isImmutable())
2625 {
2626 Capture(&bufferRegenCalls[bufferID],
2627 CaptureBindBuffer(replayState, true, gl::BufferBinding::Array, *id));
2628 Capture(
2629 &bufferRegenCalls[bufferID],
2630 CaptureBufferStorageEXT(replayState, true, gl::BufferBinding::Array,
2631 static_cast<GLsizeiptr>(buffer->getSize()),
2632 buffer->getMapPointer(), buffer->getStorageExtUsageFlags()));
2633 }
2634
2635 // Track calls to restore a given buffer's contents
2636 ResourceCalls &bufferRestoreCalls = trackedBuffers.getResourceRestoreCalls();
2637 Capture(&bufferRestoreCalls[bufferID],
2638 CaptureBindBuffer(replayState, true, gl::BufferBinding::Array, *id));
2639
2640 // Mutable buffers will be restored here using glBufferData.
2641 // Immutable buffers need to be restored below, after maping.
2642 if (!buffer->isImmutable())
2643 {
2644 Capture(&bufferRestoreCalls[bufferID],
2645 CaptureBufferData(replayState, true, gl::BufferBinding::Array,
2646 static_cast<GLsizeiptr>(buffer->getSize()),
2647 buffer->getMapPointer(), buffer->getUsage()));
2648 }
2649
2650 if (buffer->isMapped())
2651 {
2652 // Track calls to remap a buffer that started as mapped
2653 BufferCalls &bufferMapCalls = resourceTracker->getBufferMapCalls();
2654
2655 Capture(&bufferMapCalls[bufferID],
2656 CaptureBindBuffer(replayState, true, gl::BufferBinding::Array, *id));
2657
2658 void *dontCare = nullptr;
2659 Capture(&bufferMapCalls[bufferID],
2660 CaptureMapBufferRange(replayState, true, gl::BufferBinding::Array,
2661 static_cast<GLsizeiptr>(buffer->getMapOffset()),
2662 static_cast<GLsizeiptr>(buffer->getMapLength()),
2663 buffer->getAccessFlags(), dontCare));
2664
2665 // Track the bufferID that was just mapped
2666 bufferMapCalls[bufferID].back().params.setMappedBufferID(buffer->id());
2667
2668 // Restore immutable mapped buffers. Needs to happen after mapping.
2669 if (buffer->isImmutable())
2670 {
2671 ParamBuffer dataParamBuffer;
2672 dataParamBuffer.addValueParam("dest", ParamType::TGLuint, buffer->id().value);
2673 ParamCapture captureData("source", ParamType::TvoidConstPointer);
2674 CaptureMemory(buffer->getMapPointer(), static_cast<GLsizeiptr>(buffer->getSize()),
2675 &captureData);
2676 dataParamBuffer.addParam(std::move(captureData));
2677 dataParamBuffer.addValueParam<GLsizeiptr>("size", ParamType::TGLsizeiptr,
2678 static_cast<GLsizeiptr>(buffer->getSize()));
2679 bufferMapCalls[bufferID].emplace_back("UpdateClientBufferData",
2680 std::move(dataParamBuffer));
2681 }
2682 }
2683
2684 // Track calls unmap a buffer that started as unmapped
2685 BufferCalls &bufferUnmapCalls = resourceTracker->getBufferUnmapCalls();
2686 Capture(&bufferUnmapCalls[bufferID],
2687 CaptureBindBuffer(replayState, true, gl::BufferBinding::Array, *id));
2688 Capture(&bufferUnmapCalls[bufferID],
2689 CaptureUnmapBuffer(replayState, true, gl::BufferBinding::Array, GL_TRUE));
2690 }
2691
CaptureFenceSyncResetCalls(const gl::State & replayState,ResourceTracker * resourceTracker,GLsync syncID,const gl::Sync * sync)2692 void CaptureFenceSyncResetCalls(const gl::State &replayState,
2693 ResourceTracker *resourceTracker,
2694 GLsync syncID,
2695 const gl::Sync *sync)
2696 {
2697 // Track calls to regenerate a given fence sync
2698 FenceSyncCalls &fenceSyncRegenCalls = resourceTracker->getFenceSyncRegenCalls();
2699 Capture(&fenceSyncRegenCalls[syncID],
2700 CaptureFenceSync(replayState, true, sync->getCondition(), sync->getFlags(), syncID));
2701 MaybeCaptureUpdateResourceIDs(resourceTracker, &fenceSyncRegenCalls[syncID]);
2702 }
2703
CaptureBufferBindingResetCalls(const gl::State & replayState,ResourceTracker * resourceTracker,gl::BufferBinding binding,gl::BufferID id)2704 void CaptureBufferBindingResetCalls(const gl::State &replayState,
2705 ResourceTracker *resourceTracker,
2706 gl::BufferBinding binding,
2707 gl::BufferID id)
2708 {
2709 std::vector<CallCapture> &bufferBindingCalls = resourceTracker->getBufferBindingCalls();
2710 Capture(&bufferBindingCalls, CaptureBindBuffer(replayState, true, binding, id));
2711 }
2712
CaptureIndexedBuffers(const gl::State & glState,const gl::BufferVector & indexedBuffers,gl::BufferBinding binding,std::vector<CallCapture> * setupCalls)2713 void CaptureIndexedBuffers(const gl::State &glState,
2714 const gl::BufferVector &indexedBuffers,
2715 gl::BufferBinding binding,
2716 std::vector<CallCapture> *setupCalls)
2717 {
2718 for (unsigned int index = 0; index < indexedBuffers.size(); ++index)
2719 {
2720 const gl::OffsetBindingPointer<gl::Buffer> &buffer = indexedBuffers[index];
2721
2722 if (buffer.get() == nullptr)
2723 {
2724 continue;
2725 }
2726
2727 GLintptr offset = buffer.getOffset();
2728 GLsizeiptr size = buffer.getSize();
2729 gl::BufferID bufferID = buffer.get()->id();
2730
2731 // Context::bindBufferBase() calls Context::bindBufferRange() with size and offset = 0.
2732 if ((offset == 0) && (size == 0))
2733 {
2734 Capture(setupCalls, CaptureBindBufferBase(glState, true, binding, index, bufferID));
2735 }
2736 else
2737 {
2738 Capture(setupCalls,
2739 CaptureBindBufferRange(glState, true, binding, index, bufferID, offset, size));
2740 }
2741 }
2742 }
2743
CaptureDefaultVertexAttribs(const gl::State & replayState,const gl::State & apiState,std::vector<CallCapture> * setupCalls)2744 void CaptureDefaultVertexAttribs(const gl::State &replayState,
2745 const gl::State &apiState,
2746 std::vector<CallCapture> *setupCalls)
2747 {
2748 const std::vector<gl::VertexAttribCurrentValueData> ¤tValues =
2749 apiState.getVertexAttribCurrentValues();
2750
2751 for (GLuint attribIndex = 0; attribIndex < currentValues.size(); ++attribIndex)
2752 {
2753 const gl::VertexAttribCurrentValueData &defaultValue = currentValues[attribIndex];
2754 if (!IsDefaultCurrentValue(defaultValue))
2755 {
2756 Capture(setupCalls, CaptureVertexAttrib4fv(replayState, true, attribIndex,
2757 defaultValue.Values.FloatValues));
2758 }
2759 }
2760 }
2761
2762 // Capture the setup of the state that's shared by all of the contexts in the share group:
2763 // OpenGL ES Version 3.2 (October 22, 2019)
2764 // Chapter 5 Shared Objects and Multiple Contexts
2765 // Objects that can be shared between contexts include buffer objects, program
2766 // and shader objects, renderbuffer objects, sampler objects, sync objects, and texture
2767 // objects (except for the texture objects named zero).
2768 // Objects which contain references to other objects include framebuffer, program
2769 // pipeline, transform feedback, and vertex array objects. Such objects are called
2770 // container objects and are not shared.
CaptureShareGroupMidExecutionSetup(const gl::Context * context,std::vector<CallCapture> * setupCalls,ResourceTracker * resourceTracker,gl::State & replayState)2771 void CaptureShareGroupMidExecutionSetup(const gl::Context *context,
2772 std::vector<CallCapture> *setupCalls,
2773 ResourceTracker *resourceTracker,
2774 gl::State &replayState)
2775 {
2776
2777 FrameCaptureShared *frameCaptureShared = context->getShareGroup()->getFrameCaptureShared();
2778 const gl::State &apiState = context->getState();
2779
2780 // Small helper function to make the code more readable.
2781 auto cap = [setupCalls](CallCapture &&call) { setupCalls->emplace_back(std::move(call)); };
2782
2783 // Capture Buffer data.
2784 const gl::BufferManager &buffers = apiState.getBufferManagerForCapture();
2785 for (const auto &bufferIter : buffers)
2786 {
2787 gl::BufferID id = {bufferIter.first};
2788 gl::Buffer *buffer = bufferIter.second;
2789
2790 if (id.value == 0)
2791 {
2792 continue;
2793 }
2794
2795 // Generate binding.
2796 cap(CaptureGenBuffers(replayState, true, 1, &id));
2797
2798 resourceTracker->getTrackedResource(ResourceIDType::Buffer)
2799 .getStartingResources()
2800 .insert(id.value);
2801
2802 MaybeCaptureUpdateResourceIDs(resourceTracker, setupCalls);
2803
2804 // glBufferData. Would possibly be better implemented using a getData impl method.
2805 // Saving buffers that are mapped during a swap is not yet handled.
2806 if (buffer->getSize() == 0)
2807 {
2808 resourceTracker->setStartingBufferMapped(buffer->id().value, false);
2809 continue;
2810 }
2811
2812 // Remember if the buffer was already mapped
2813 GLboolean bufferMapped = buffer->isMapped();
2814
2815 // If needed, map the buffer so we can capture its contents
2816 if (!bufferMapped)
2817 {
2818 (void)buffer->mapRange(context, 0, static_cast<GLsizeiptr>(buffer->getSize()),
2819 GL_MAP_READ_BIT);
2820 }
2821
2822 // Always use the array buffer binding point to upload data to keep things simple.
2823 if (buffer != replayState.getArrayBuffer())
2824 {
2825 replayState.setBufferBinding(context, gl::BufferBinding::Array, buffer);
2826 cap(CaptureBindBuffer(replayState, true, gl::BufferBinding::Array, id));
2827 }
2828
2829 if (buffer->isImmutable())
2830 {
2831 cap(CaptureBufferStorageEXT(replayState, true, gl::BufferBinding::Array,
2832 static_cast<GLsizeiptr>(buffer->getSize()),
2833 buffer->getMapPointer(),
2834 buffer->getStorageExtUsageFlags()));
2835 }
2836 else
2837 {
2838 cap(CaptureBufferData(replayState, true, gl::BufferBinding::Array,
2839 static_cast<GLsizeiptr>(buffer->getSize()),
2840 buffer->getMapPointer(), buffer->getUsage()));
2841 }
2842
2843 if (bufferMapped)
2844 {
2845 void *dontCare = nullptr;
2846 Capture(setupCalls,
2847 CaptureMapBufferRange(replayState, true, gl::BufferBinding::Array,
2848 static_cast<GLsizeiptr>(buffer->getMapOffset()),
2849 static_cast<GLsizeiptr>(buffer->getMapLength()),
2850 buffer->getAccessFlags(), dontCare));
2851
2852 resourceTracker->setStartingBufferMapped(buffer->id().value, true);
2853
2854 frameCaptureShared->trackBufferMapping(
2855 &setupCalls->back(), buffer->id(), buffer,
2856 static_cast<GLsizeiptr>(buffer->getMapOffset()),
2857 static_cast<GLsizeiptr>(buffer->getMapLength()),
2858 (buffer->getAccessFlags() & GL_MAP_WRITE_BIT) != 0,
2859 (buffer->getStorageExtUsageFlags() & GL_MAP_COHERENT_BIT_EXT) != 0);
2860 }
2861 else
2862 {
2863 resourceTracker->setStartingBufferMapped(buffer->id().value, false);
2864 }
2865
2866 // Generate the calls needed to restore this buffer to original state for frame looping
2867 CaptureBufferResetCalls(replayState, resourceTracker, &id, buffer);
2868
2869 // Unmap the buffer if it wasn't already mapped
2870 if (!bufferMapped)
2871 {
2872 GLboolean dontCare;
2873 (void)buffer->unmap(context, &dontCare);
2874 }
2875 }
2876
2877 // Clear the array buffer binding.
2878 if (replayState.getTargetBuffer(gl::BufferBinding::Array))
2879 {
2880 cap(CaptureBindBuffer(replayState, true, gl::BufferBinding::Array, {0}));
2881 replayState.setBufferBinding(context, gl::BufferBinding::Array, nullptr);
2882 }
2883
2884 // Set a unpack alignment of 1. Otherwise, computeRowPitch() will compute the wrong value,
2885 // leading to a crash in memcpy() when capturing the texture contents.
2886 gl::PixelUnpackState ¤tUnpackState = replayState.getUnpackState();
2887 if (currentUnpackState.alignment != 1)
2888 {
2889 cap(CapturePixelStorei(replayState, true, GL_UNPACK_ALIGNMENT, 1));
2890 currentUnpackState.alignment = 1;
2891 }
2892
2893 // Capture Texture setup and data.
2894 const gl::TextureManager &textures = apiState.getTextureManagerForCapture();
2895
2896 for (const auto &textureIter : textures)
2897 {
2898 gl::TextureID id = {textureIter.first};
2899 gl::Texture *texture = textureIter.second;
2900
2901 if (id.value == 0)
2902 {
2903 continue;
2904 }
2905
2906 // Track this as a starting resource that may need to be restored.
2907 TrackedResource &trackedTextures =
2908 resourceTracker->getTrackedResource(ResourceIDType::Texture);
2909 ResourceSet &startingTextures = trackedTextures.getStartingResources();
2910 startingTextures.insert(id.value);
2911
2912 // For the initial texture creation calls, track in the generate list
2913 ResourceCalls &textureRegenCalls = trackedTextures.getResourceRegenCalls();
2914 CallVector texGenCalls({setupCalls, &textureRegenCalls[id.value]});
2915
2916 // Gen the Texture.
2917 for (std::vector<CallCapture> *calls : texGenCalls)
2918 {
2919 Capture(calls, CaptureGenTextures(replayState, true, 1, &id));
2920 MaybeCaptureUpdateResourceIDs(resourceTracker, calls);
2921 }
2922
2923 // For the remaining texture setup calls, track in the restore list
2924 ResourceCalls &textureRestoreCalls = trackedTextures.getResourceRestoreCalls();
2925 CallVector texSetupCalls({setupCalls, &textureRestoreCalls[id.value]});
2926
2927 for (std::vector<CallCapture> *calls : texSetupCalls)
2928 {
2929 Capture(calls, CaptureBindTexture(replayState, true, texture->getType(), id));
2930 }
2931 replayState.setSamplerTexture(context, texture->getType(), texture);
2932
2933 // Capture sampler parameter states.
2934 // TODO(jmadill): More sampler / texture states. http://anglebug.com/3662
2935 gl::SamplerState defaultSamplerState =
2936 gl::SamplerState::CreateDefaultForTarget(texture->getType());
2937 const gl::SamplerState &textureSamplerState = texture->getSamplerState();
2938
2939 auto capTexParam = [&replayState, texture, &texSetupCalls](GLenum pname, GLint param) {
2940 for (std::vector<CallCapture> *calls : texSetupCalls)
2941 {
2942 Capture(calls,
2943 CaptureTexParameteri(replayState, true, texture->getType(), pname, param));
2944 }
2945 };
2946
2947 auto capTexParamf = [&replayState, texture, &texSetupCalls](GLenum pname, GLfloat param) {
2948 for (std::vector<CallCapture> *calls : texSetupCalls)
2949 {
2950 Capture(calls,
2951 CaptureTexParameterf(replayState, true, texture->getType(), pname, param));
2952 }
2953 };
2954
2955 if (textureSamplerState.getMinFilter() != defaultSamplerState.getMinFilter())
2956 {
2957 capTexParam(GL_TEXTURE_MIN_FILTER, textureSamplerState.getMinFilter());
2958 }
2959
2960 if (textureSamplerState.getMagFilter() != defaultSamplerState.getMagFilter())
2961 {
2962 capTexParam(GL_TEXTURE_MAG_FILTER, textureSamplerState.getMagFilter());
2963 }
2964
2965 if (textureSamplerState.getWrapR() != defaultSamplerState.getWrapR())
2966 {
2967 capTexParam(GL_TEXTURE_WRAP_R, textureSamplerState.getWrapR());
2968 }
2969
2970 if (textureSamplerState.getWrapS() != defaultSamplerState.getWrapS())
2971 {
2972 capTexParam(GL_TEXTURE_WRAP_S, textureSamplerState.getWrapS());
2973 }
2974
2975 if (textureSamplerState.getWrapT() != defaultSamplerState.getWrapT())
2976 {
2977 capTexParam(GL_TEXTURE_WRAP_T, textureSamplerState.getWrapT());
2978 }
2979
2980 if (textureSamplerState.getMinLod() != defaultSamplerState.getMinLod())
2981 {
2982 capTexParamf(GL_TEXTURE_MIN_LOD, textureSamplerState.getMinLod());
2983 }
2984
2985 if (textureSamplerState.getMaxLod() != defaultSamplerState.getMaxLod())
2986 {
2987 capTexParamf(GL_TEXTURE_MAX_LOD, textureSamplerState.getMaxLod());
2988 }
2989
2990 if (textureSamplerState.getCompareMode() != defaultSamplerState.getCompareMode())
2991 {
2992 capTexParam(GL_TEXTURE_COMPARE_MODE, textureSamplerState.getCompareMode());
2993 }
2994
2995 if (textureSamplerState.getCompareFunc() != defaultSamplerState.getCompareFunc())
2996 {
2997 capTexParam(GL_TEXTURE_COMPARE_FUNC, textureSamplerState.getCompareFunc());
2998 }
2999
3000 // Texture parameters
3001 if (texture->getSwizzleRed() != GL_RED)
3002 {
3003 capTexParam(GL_TEXTURE_SWIZZLE_R, texture->getSwizzleRed());
3004 }
3005
3006 if (texture->getSwizzleGreen() != GL_GREEN)
3007 {
3008 capTexParam(GL_TEXTURE_SWIZZLE_G, texture->getSwizzleGreen());
3009 }
3010
3011 if (texture->getSwizzleBlue() != GL_BLUE)
3012 {
3013 capTexParam(GL_TEXTURE_SWIZZLE_B, texture->getSwizzleBlue());
3014 }
3015
3016 if (texture->getSwizzleAlpha() != GL_ALPHA)
3017 {
3018 capTexParam(GL_TEXTURE_SWIZZLE_A, texture->getSwizzleAlpha());
3019 }
3020
3021 if (texture->getBaseLevel() != 0)
3022 {
3023 capTexParam(GL_TEXTURE_BASE_LEVEL, texture->getBaseLevel());
3024 }
3025
3026 if (texture->getMaxLevel() != 1000)
3027 {
3028 capTexParam(GL_TEXTURE_MAX_LEVEL, texture->getMaxLevel());
3029 }
3030
3031 // If the texture is immutable, initialize it with TexStorage
3032 if (texture->getImmutableFormat())
3033 {
3034 // We can only call TexStorage *once* on an immutable texture, so it needs special
3035 // handling. To solve this, immutable textures will have a BindTexture and TexStorage as
3036 // part of their textureRegenCalls. The resulting regen sequence will be:
3037 //
3038 // const GLuint glDeleteTextures_texturesPacked_0[] = { gTextureMap[52] };
3039 // glDeleteTextures(1, glDeleteTextures_texturesPacked_0);
3040 // glGenTextures(1, reinterpret_cast<GLuint *>(gReadBuffer));
3041 // UpdateTextureID(52, 0);
3042 // glBindTexture(GL_TEXTURE_2D, gTextureMap[52]);
3043 // glTexStorage2D(GL_TEXTURE_2D, 1, GL_R8, 256, 512);
3044
3045 // Bind the texture first just for textureRegenCalls
3046 Capture(&textureRegenCalls[id.value],
3047 CaptureBindTexture(replayState, true, texture->getType(), id));
3048
3049 // Then add TexStorage to texGenCalls instead of texSetupCalls
3050 for (std::vector<CallCapture> *calls : texGenCalls)
3051 {
3052 CaptureTextureStorage(calls, &replayState, texture);
3053 }
3054 }
3055
3056 // Iterate texture levels and layers.
3057 gl::ImageIndexIterator imageIter = gl::ImageIndexIterator::MakeGeneric(
3058 texture->getType(), 0, texture->getMipmapMaxLevel() + 1, gl::ImageIndex::kEntireLevel,
3059 gl::ImageIndex::kEntireLevel);
3060 while (imageIter.hasNext())
3061 {
3062 gl::ImageIndex index = imageIter.next();
3063
3064 const gl::ImageDesc &desc = texture->getTextureState().getImageDesc(index);
3065
3066 if (desc.size.empty())
3067 {
3068 continue;
3069 }
3070
3071 const gl::InternalFormat &format = *desc.format.info;
3072
3073 bool supportedType = (index.getType() == gl::TextureType::_2D ||
3074 index.getType() == gl::TextureType::_3D ||
3075 index.getType() == gl::TextureType::_2DArray ||
3076 index.getType() == gl::TextureType::Buffer ||
3077 index.getType() == gl::TextureType::CubeMap ||
3078 index.getType() == gl::TextureType::CubeMapArray);
3079
3080 // Check for supported textures
3081 if (!supportedType)
3082 {
3083 ERR() << "Unsupported texture type: " << index.getType();
3084 UNREACHABLE();
3085 }
3086
3087 if (index.getType() == gl::TextureType::Buffer)
3088 {
3089 // The buffer contents are already backed up, but we need to emit the TexBuffer
3090 // binding calls
3091 for (std::vector<CallCapture> *calls : texSetupCalls)
3092 {
3093 CaptureTextureContents(calls, &replayState, texture, index, desc, 0, 0);
3094 }
3095 continue;
3096 }
3097
3098 if (context->getExtensions().getImageANGLE)
3099 {
3100 // Use ANGLE_get_image to read back pixel data.
3101 angle::MemoryBuffer data;
3102
3103 const gl::Extents extents(desc.size.width, desc.size.height, desc.size.depth);
3104
3105 gl::PixelPackState packState;
3106 packState.alignment = 1;
3107
3108 if (format.compressed)
3109 {
3110 // Calculate the size needed to store the compressed level
3111 GLuint sizeInBytes;
3112 bool result = format.computeCompressedImageSize(extents, &sizeInBytes);
3113 ASSERT(result);
3114
3115 result = data.resize(sizeInBytes);
3116 ASSERT(result);
3117
3118 (void)texture->getCompressedTexImage(context, packState, nullptr,
3119 index.getTarget(), index.getLevelIndex(),
3120 data.data());
3121 }
3122 else
3123 {
3124 GLenum getFormat = format.format;
3125 GLenum getType = format.type;
3126
3127 const gl::PixelUnpackState &unpack = apiState.getUnpackState();
3128
3129 GLuint endByte = 0;
3130 bool unpackSize =
3131 format.computePackUnpackEndByte(getType, extents, unpack, true, &endByte);
3132 ASSERT(unpackSize);
3133
3134 bool result = data.resize(endByte);
3135 ASSERT(result);
3136
3137 (void)texture->getTexImage(context, packState, nullptr, index.getTarget(),
3138 index.getLevelIndex(), getFormat, getType,
3139 data.data());
3140 }
3141
3142 for (std::vector<CallCapture> *calls : texSetupCalls)
3143 {
3144 CaptureTextureContents(calls, &replayState, texture, index, desc,
3145 static_cast<GLuint>(data.size()), data.data());
3146 }
3147 }
3148 else
3149 {
3150 for (std::vector<CallCapture> *calls : texSetupCalls)
3151 {
3152 CaptureTextureContents(calls, &replayState, texture, index, desc, 0, nullptr);
3153 }
3154 }
3155 }
3156 }
3157
3158 // Capture Renderbuffers.
3159 const gl::RenderbufferManager &renderbuffers = apiState.getRenderbufferManagerForCapture();
3160 FramebufferCaptureFuncs framebufferFuncs(context->isGLES1());
3161
3162 for (const auto &renderbufIter : renderbuffers)
3163 {
3164 gl::RenderbufferID id = {renderbufIter.first};
3165 const gl::Renderbuffer *renderbuffer = renderbufIter.second;
3166
3167 // Generate renderbuffer id.
3168 cap(framebufferFuncs.genRenderbuffers(replayState, true, 1, &id));
3169
3170 resourceTracker->getTrackedResource(ResourceIDType::Renderbuffer)
3171 .getStartingResources()
3172 .insert(id.value);
3173
3174 MaybeCaptureUpdateResourceIDs(resourceTracker, setupCalls);
3175 cap(framebufferFuncs.bindRenderbuffer(replayState, true, GL_RENDERBUFFER, id));
3176
3177 GLenum internalformat = renderbuffer->getFormat().info->internalFormat;
3178
3179 if (renderbuffer->getSamples() > 0)
3180 {
3181 // Note: We could also use extensions if available.
3182 cap(CaptureRenderbufferStorageMultisample(
3183 replayState, true, GL_RENDERBUFFER, renderbuffer->getSamples(), internalformat,
3184 renderbuffer->getWidth(), renderbuffer->getHeight()));
3185 }
3186 else
3187 {
3188 cap(framebufferFuncs.renderbufferStorage(replayState, true, GL_RENDERBUFFER,
3189 internalformat, renderbuffer->getWidth(),
3190 renderbuffer->getHeight()));
3191 }
3192
3193 // TODO(jmadill): Capture renderbuffer contents. http://anglebug.com/3662
3194 }
3195
3196 // Capture Shaders and Programs.
3197 const gl::ShaderProgramManager &shadersAndPrograms =
3198 apiState.getShaderProgramManagerForCapture();
3199 const gl::ResourceMap<gl::Shader, gl::ShaderProgramID> &shaders =
3200 shadersAndPrograms.getShadersForCapture();
3201 const gl::ResourceMap<gl::Program, gl::ShaderProgramID> &programs =
3202 shadersAndPrograms.getProgramsForCaptureAndPerf();
3203
3204 // Capture Program binary state.
3205 gl::ShaderProgramID tempShaderStartID = {resourceTracker->getMaxShaderPrograms()};
3206 for (const auto &programIter : programs)
3207 {
3208 gl::ShaderProgramID id = {programIter.first};
3209 gl::Program *program = programIter.second;
3210
3211 // Unlinked programs don't have an executable. Thus they don't need to be captured.
3212 // Programs are shared by contexts in the share group and only need to be captured once.
3213 if (!program->isLinked())
3214 {
3215 continue;
3216 }
3217
3218 size_t programSetupStart = setupCalls->size();
3219
3220 // Get last linked shader source.
3221 const ProgramSources &linkedSources =
3222 context->getShareGroup()->getFrameCaptureShared()->getProgramSources(id);
3223
3224 cap(CaptureCreateProgram(replayState, true, id.value));
3225
3226 GenerateLinkedProgram(context, replayState, resourceTracker, setupCalls, program, id,
3227 tempShaderStartID, linkedSources);
3228
3229 // Update the program in replayState
3230 if (!replayState.getProgram() || replayState.getProgram()->id() != program->id())
3231 {
3232 // Note: We don't do this in GenerateLinkedProgram because it can't modify state
3233 (void)replayState.setProgram(context, program);
3234 }
3235
3236 resourceTracker->getTrackedResource(ResourceIDType::ShaderProgram)
3237 .getStartingResources()
3238 .insert(id.value);
3239
3240 size_t programSetupEnd = setupCalls->size();
3241
3242 // Mark the range of calls used to setup this program
3243 frameCaptureShared->markResourceSetupCallsInactive(
3244 setupCalls, ResourceIDType::ShaderProgram, id.value,
3245 gl::Range<size_t>(programSetupStart, programSetupEnd));
3246 }
3247
3248 // Handle shaders.
3249 for (const auto &shaderIter : shaders)
3250 {
3251 gl::ShaderProgramID id = {shaderIter.first};
3252 gl::Shader *shader = shaderIter.second;
3253
3254 // Skip shaders scheduled for deletion.
3255 // Shaders are shared by contexts in the share group and only need to be captured once.
3256 if (shader->hasBeenDeleted())
3257 {
3258 continue;
3259 }
3260
3261 size_t shaderSetupStart = setupCalls->size();
3262
3263 cap(CaptureCreateShader(replayState, true, shader->getType(), id.value));
3264
3265 std::string shaderSource = shader->getSourceString();
3266 const char *sourcePointer = shaderSource.empty() ? nullptr : shaderSource.c_str();
3267
3268 // This does not handle some more tricky situations like attaching shaders to a non-linked
3269 // program. Or attaching uncompiled shaders. Or attaching and then deleting a shader.
3270 // TODO(jmadill): Handle trickier program uses. http://anglebug.com/3662
3271 if (shader->isCompiled())
3272 {
3273 const std::string &capturedSource =
3274 context->getShareGroup()->getFrameCaptureShared()->getShaderSource(id);
3275 if (capturedSource != shaderSource)
3276 {
3277 ASSERT(!capturedSource.empty());
3278 sourcePointer = capturedSource.c_str();
3279 }
3280
3281 cap(CaptureShaderSource(replayState, true, id, 1, &sourcePointer, nullptr));
3282 cap(CaptureCompileShader(replayState, true, id));
3283 }
3284
3285 if (sourcePointer && (!shader->isCompiled() || sourcePointer != shaderSource.c_str()))
3286 {
3287 cap(CaptureShaderSource(replayState, true, id, 1, &sourcePointer, nullptr));
3288 }
3289
3290 size_t shaderSetupEnd = setupCalls->size();
3291
3292 // Mark the range of calls used to setup this shader
3293 frameCaptureShared->markResourceSetupCallsInactive(
3294 setupCalls, ResourceIDType::ShaderProgram, id.value,
3295 gl::Range<size_t>(shaderSetupStart, shaderSetupEnd));
3296 }
3297
3298 // Capture Sampler Objects
3299 const gl::SamplerManager &samplers = apiState.getSamplerManagerForCapture();
3300 for (const auto &samplerIter : samplers)
3301 {
3302 gl::SamplerID samplerID = {samplerIter.first};
3303
3304 // Don't gen the sampler if we've seen it before, since they are shared across the context
3305 // share group.
3306 cap(CaptureGenSamplers(replayState, true, 1, &samplerID));
3307 MaybeCaptureUpdateResourceIDs(resourceTracker, setupCalls);
3308
3309 gl::Sampler *sampler = samplerIter.second;
3310 if (!sampler)
3311 {
3312 continue;
3313 }
3314
3315 gl::SamplerState defaultSamplerState;
3316 if (sampler->getMinFilter() != defaultSamplerState.getMinFilter())
3317 {
3318 cap(CaptureSamplerParameteri(replayState, true, samplerID, GL_TEXTURE_MIN_FILTER,
3319 sampler->getMinFilter()));
3320 }
3321 if (sampler->getMagFilter() != defaultSamplerState.getMagFilter())
3322 {
3323 cap(CaptureSamplerParameteri(replayState, true, samplerID, GL_TEXTURE_MAG_FILTER,
3324 sampler->getMagFilter()));
3325 }
3326 if (sampler->getWrapS() != defaultSamplerState.getWrapS())
3327 {
3328 cap(CaptureSamplerParameteri(replayState, true, samplerID, GL_TEXTURE_WRAP_S,
3329 sampler->getWrapS()));
3330 }
3331 if (sampler->getWrapR() != defaultSamplerState.getWrapR())
3332 {
3333 cap(CaptureSamplerParameteri(replayState, true, samplerID, GL_TEXTURE_WRAP_R,
3334 sampler->getWrapR()));
3335 }
3336 if (sampler->getWrapT() != defaultSamplerState.getWrapT())
3337 {
3338 cap(CaptureSamplerParameteri(replayState, true, samplerID, GL_TEXTURE_WRAP_T,
3339 sampler->getWrapT()));
3340 }
3341 if (sampler->getMinLod() != defaultSamplerState.getMinLod())
3342 {
3343 cap(CaptureSamplerParameterf(replayState, true, samplerID, GL_TEXTURE_MIN_LOD,
3344 sampler->getMinLod()));
3345 }
3346 if (sampler->getMaxLod() != defaultSamplerState.getMaxLod())
3347 {
3348 cap(CaptureSamplerParameterf(replayState, true, samplerID, GL_TEXTURE_MAX_LOD,
3349 sampler->getMaxLod()));
3350 }
3351 if (sampler->getCompareMode() != defaultSamplerState.getCompareMode())
3352 {
3353 cap(CaptureSamplerParameteri(replayState, true, samplerID, GL_TEXTURE_COMPARE_MODE,
3354 sampler->getCompareMode()));
3355 }
3356 if (sampler->getCompareFunc() != defaultSamplerState.getCompareFunc())
3357 {
3358 cap(CaptureSamplerParameteri(replayState, true, samplerID, GL_TEXTURE_COMPARE_FUNC,
3359 sampler->getCompareFunc()));
3360 }
3361 }
3362
3363 // Capture Sync Objects
3364 const gl::SyncManager &syncs = apiState.getSyncManagerForCapture();
3365 for (const auto &syncIter : syncs)
3366 {
3367 GLsync syncID = gl::bitCast<GLsync>(static_cast<size_t>(syncIter.first));
3368 const gl::Sync *sync = syncIter.second;
3369
3370 if (!sync)
3371 {
3372 continue;
3373 }
3374 cap(CaptureFenceSync(replayState, true, sync->getCondition(), sync->getFlags(), syncID));
3375 CaptureFenceSyncResetCalls(replayState, resourceTracker, syncID, sync);
3376 resourceTracker->getStartingFenceSyncs().insert(syncID);
3377 }
3378 }
3379
CaptureMidExecutionSetup(const gl::Context * context,std::vector<CallCapture> * setupCalls,std::vector<CallCapture> * shareGroupSetupCalls,ResourceIDToSetupCallsMap * resourceIDToSetupCalls,ResourceTracker * resourceTracker,gl::State & replayState,bool validationEnabled)3380 void CaptureMidExecutionSetup(const gl::Context *context,
3381 std::vector<CallCapture> *setupCalls,
3382 std::vector<CallCapture> *shareGroupSetupCalls,
3383 ResourceIDToSetupCallsMap *resourceIDToSetupCalls,
3384 ResourceTracker *resourceTracker,
3385 gl::State &replayState,
3386 bool validationEnabled)
3387 {
3388 const gl::State &apiState = context->getState();
3389
3390 // Small helper function to make the code more readable.
3391 auto cap = [setupCalls](CallCapture &&call) { setupCalls->emplace_back(std::move(call)); };
3392
3393 // Need to go from uint32 -> uint64 -> EGLContext (void*) to handle MSVC compiler
3394 // warning on 64b systems:
3395 // error C4312: 'reinterpret_cast': conversion from 'uint32_t' to 'EGLContext' of
3396 // greater size
3397 uint64_t contextID = static_cast<uint64_t>(context->id().value);
3398 EGLContext eglContext = reinterpret_cast<EGLContext>(contextID);
3399 cap(CaptureMakeCurrent(EGL_NO_DISPLAY, EGL_NO_SURFACE, EGL_NO_SURFACE, eglContext));
3400
3401 // Vertex input states. Must happen after buffer data initialization. Do not capture on GLES1.
3402 if (!context->isGLES1())
3403 {
3404 CaptureDefaultVertexAttribs(replayState, apiState, setupCalls);
3405 }
3406
3407 // Capture vertex array objects
3408 const gl::VertexArrayMap &vertexArrayMap = context->getVertexArraysForCapture();
3409 gl::VertexArrayID boundVertexArrayID = {0};
3410 for (const auto &vertexArrayIter : vertexArrayMap)
3411 {
3412 gl::VertexArrayID vertexArrayID = {vertexArrayIter.first};
3413 if (vertexArrayID.value != 0)
3414 {
3415 cap(CaptureGenVertexArrays(replayState, true, 1, &vertexArrayID));
3416
3417 resourceTracker->getTrackedResource(ResourceIDType::VertexArray)
3418 .getStartingResources()
3419 .insert(vertexArrayID.value);
3420
3421 MaybeCaptureUpdateResourceIDs(resourceTracker, setupCalls);
3422 }
3423
3424 if (vertexArrayIter.second)
3425 {
3426 const gl::VertexArray *vertexArray = vertexArrayIter.second;
3427
3428 // Bind the vertexArray (unless default) and populate it
3429 if (vertexArrayID.value != 0)
3430 {
3431 cap(CaptureBindVertexArray(replayState, true, vertexArrayID));
3432 boundVertexArrayID = vertexArrayID;
3433 }
3434 CaptureVertexArrayState(setupCalls, context, vertexArray, &replayState);
3435 }
3436 }
3437
3438 // Bind the current vertex array
3439 const gl::VertexArray *currentVertexArray = apiState.getVertexArray();
3440 if (currentVertexArray->id() != boundVertexArrayID)
3441 {
3442 cap(CaptureBindVertexArray(replayState, true, currentVertexArray->id()));
3443 }
3444
3445 // Capture indexed buffer bindings.
3446 const gl::BufferVector &uniformIndexedBuffers =
3447 apiState.getOffsetBindingPointerUniformBuffers();
3448 const gl::BufferVector &atomicCounterIndexedBuffers =
3449 apiState.getOffsetBindingPointerAtomicCounterBuffers();
3450 const gl::BufferVector &shaderStorageIndexedBuffers =
3451 apiState.getOffsetBindingPointerShaderStorageBuffers();
3452 CaptureIndexedBuffers(replayState, uniformIndexedBuffers, gl::BufferBinding::Uniform,
3453 setupCalls);
3454 CaptureIndexedBuffers(replayState, atomicCounterIndexedBuffers,
3455 gl::BufferBinding::AtomicCounter, setupCalls);
3456 CaptureIndexedBuffers(replayState, shaderStorageIndexedBuffers,
3457 gl::BufferBinding::ShaderStorage, setupCalls);
3458
3459 // Capture Buffer bindings.
3460 const gl::BoundBufferMap &boundBuffers = apiState.getBoundBuffersForCapture();
3461 for (gl::BufferBinding binding : angle::AllEnums<gl::BufferBinding>())
3462 {
3463 gl::BufferID bufferID = boundBuffers[binding].id();
3464
3465 // Filter out redundant buffer binding commands. Note that the code in the previous section
3466 // only binds to ARRAY_BUFFER. Therefore we only check the array binding against the binding
3467 // we set earlier.
3468 bool isArray = binding == gl::BufferBinding::Array;
3469 const gl::Buffer *arrayBuffer = replayState.getArrayBuffer();
3470 if ((isArray && arrayBuffer && arrayBuffer->id() != bufferID) ||
3471 (!isArray && bufferID.value != 0))
3472 {
3473 cap(CaptureBindBuffer(replayState, true, binding, bufferID));
3474 replayState.setBufferBinding(context, binding, boundBuffers[binding].get());
3475 }
3476
3477 // Restore all buffer bindings for Reset
3478 if (bufferID.value != 0)
3479 {
3480 CaptureBufferBindingResetCalls(replayState, resourceTracker, binding, bufferID);
3481 }
3482 }
3483
3484 // Set a unpack alignment of 1. Otherwise, computeRowPitch() will compute the wrong value,
3485 // leading to a crash in memcpy() when capturing the texture contents.
3486 gl::PixelUnpackState ¤tUnpackState = replayState.getUnpackState();
3487 if (currentUnpackState.alignment != 1)
3488 {
3489 cap(CapturePixelStorei(replayState, true, GL_UNPACK_ALIGNMENT, 1));
3490 currentUnpackState.alignment = 1;
3491 }
3492
3493 // Capture Texture setup and data.
3494 const gl::TextureBindingMap &apiBoundTextures = apiState.getBoundTexturesForCapture();
3495
3496 // Set Texture bindings.
3497 for (gl::TextureType textureType : angle::AllEnums<gl::TextureType>())
3498 {
3499 const gl::TextureBindingVector &apiBindings = apiBoundTextures[textureType];
3500 const gl::TextureBindingVector &replayBindings =
3501 replayState.getBoundTexturesForCapture()[textureType];
3502 ASSERT(apiBindings.size() == replayBindings.size());
3503 for (size_t bindingIndex = 0; bindingIndex < apiBindings.size(); ++bindingIndex)
3504 {
3505 gl::TextureID apiTextureID = apiBindings[bindingIndex].id();
3506 gl::TextureID replayTextureID = replayBindings[bindingIndex].id();
3507
3508 if (apiTextureID != replayTextureID)
3509 {
3510 if (replayState.getActiveSampler() != bindingIndex)
3511 {
3512 cap(CaptureActiveTexture(replayState, true,
3513 GL_TEXTURE0 + static_cast<GLenum>(bindingIndex)));
3514 replayState.setActiveSampler(static_cast<unsigned int>(bindingIndex));
3515 }
3516
3517 cap(CaptureBindTexture(replayState, true, textureType, apiTextureID));
3518 replayState.setSamplerTexture(context, textureType,
3519 apiBindings[bindingIndex].get());
3520 }
3521 }
3522 }
3523
3524 // Set active Texture.
3525 if (replayState.getActiveSampler() != apiState.getActiveSampler())
3526 {
3527 cap(CaptureActiveTexture(replayState, true,
3528 GL_TEXTURE0 + static_cast<GLenum>(apiState.getActiveSampler())));
3529 replayState.setActiveSampler(apiState.getActiveSampler());
3530 }
3531
3532 // Set Renderbuffer binding.
3533 const gl::RenderbufferManager &renderbuffers = apiState.getRenderbufferManagerForCapture();
3534 gl::RenderbufferID currentRenderbuffer = {0};
3535 for (const auto &renderbufIter : renderbuffers)
3536 {
3537 currentRenderbuffer = renderbufIter.second->id();
3538 }
3539
3540 if (currentRenderbuffer != apiState.getRenderbufferId())
3541 {
3542 cap(CaptureBindRenderbuffer(replayState, true, GL_RENDERBUFFER,
3543 apiState.getRenderbufferId()));
3544 }
3545
3546 // Capture Framebuffers.
3547 const gl::FramebufferManager &framebuffers = apiState.getFramebufferManagerForCapture();
3548 FramebufferCaptureFuncs framebufferFuncs(context->isGLES1());
3549
3550 gl::FramebufferID currentDrawFramebuffer = {0};
3551 gl::FramebufferID currentReadFramebuffer = {0};
3552
3553 for (const auto &framebufferIter : framebuffers)
3554 {
3555 gl::FramebufferID id = {framebufferIter.first};
3556 const gl::Framebuffer *framebuffer = framebufferIter.second;
3557
3558 // The default Framebuffer exists (by default).
3559 if (framebuffer->isDefault())
3560 {
3561 continue;
3562 }
3563
3564 // Track this as a starting resource that may need to be restored
3565 TrackedResource &trackedFramebuffers =
3566 resourceTracker->getTrackedResource(ResourceIDType::Framebuffer);
3567 ResourceSet &startingFramebuffers = trackedFramebuffers.getStartingResources();
3568 startingFramebuffers.insert(id.value);
3569
3570 // Create two lists of calls for initial setup
3571 ResourceCalls &framebufferRegenCalls = trackedFramebuffers.getResourceRegenCalls();
3572 CallVector framebufferGenCalls({setupCalls, &framebufferRegenCalls[id.value]});
3573
3574 // Gen the framebuffer
3575 for (std::vector<CallCapture> *calls : framebufferGenCalls)
3576 {
3577 Capture(calls, framebufferFuncs.genFramebuffers(replayState, true, 1, &id));
3578 MaybeCaptureUpdateResourceIDs(resourceTracker, calls);
3579 }
3580
3581 // Create two lists of calls for remaining setup calls. One for setup, and one for restore
3582 // during reset.
3583 ResourceCalls &framebufferRestoreCalls = trackedFramebuffers.getResourceRestoreCalls();
3584 CallVector framebufferSetupCalls({setupCalls, &framebufferRestoreCalls[id.value]});
3585
3586 for (std::vector<CallCapture> *calls : framebufferSetupCalls)
3587 {
3588 Capture(calls, framebufferFuncs.bindFramebuffer(replayState, true, GL_FRAMEBUFFER, id));
3589 }
3590 currentDrawFramebuffer = currentReadFramebuffer = id;
3591
3592 // Color Attachments.
3593 for (const gl::FramebufferAttachment &colorAttachment : framebuffer->getColorAttachments())
3594 {
3595 if (!colorAttachment.isAttached())
3596 {
3597 continue;
3598 }
3599
3600 for (std::vector<CallCapture> *calls : framebufferSetupCalls)
3601 {
3602 CaptureFramebufferAttachment(calls, replayState, framebufferFuncs, colorAttachment);
3603 }
3604 }
3605
3606 const gl::FramebufferAttachment *depthAttachment = framebuffer->getDepthAttachment();
3607 if (depthAttachment)
3608 {
3609 ASSERT(depthAttachment->getBinding() == GL_DEPTH_ATTACHMENT ||
3610 depthAttachment->getBinding() == GL_DEPTH_STENCIL_ATTACHMENT);
3611 for (std::vector<CallCapture> *calls : framebufferSetupCalls)
3612 {
3613 CaptureFramebufferAttachment(calls, replayState, framebufferFuncs,
3614 *depthAttachment);
3615 }
3616 }
3617
3618 const gl::FramebufferAttachment *stencilAttachment = framebuffer->getStencilAttachment();
3619 if (stencilAttachment)
3620 {
3621 ASSERT(stencilAttachment->getBinding() == GL_STENCIL_ATTACHMENT ||
3622 depthAttachment->getBinding() == GL_DEPTH_STENCIL_ATTACHMENT);
3623 for (std::vector<CallCapture> *calls : framebufferSetupCalls)
3624 {
3625 CaptureFramebufferAttachment(calls, replayState, framebufferFuncs,
3626 *stencilAttachment);
3627 }
3628 }
3629
3630 gl::FramebufferState defaultFramebufferState(
3631 context->getCaps(), framebuffer->getState().id(),
3632 framebuffer->getState().getFramebufferSerial());
3633 const std::vector<GLenum> &defaultDrawBufferStates =
3634 defaultFramebufferState.getDrawBufferStates();
3635 const std::vector<GLenum> &drawBufferStates = framebuffer->getDrawBufferStates();
3636
3637 if (drawBufferStates != defaultDrawBufferStates)
3638 {
3639 for (std::vector<CallCapture> *calls : framebufferSetupCalls)
3640 {
3641 Capture(calls, CaptureDrawBuffers(replayState, true,
3642 static_cast<GLsizei>(drawBufferStates.size()),
3643 drawBufferStates.data()));
3644 }
3645 }
3646 }
3647
3648 // Capture framebuffer bindings.
3649 if (apiState.getDrawFramebuffer())
3650 {
3651 ASSERT(apiState.getReadFramebuffer());
3652 gl::FramebufferID stateReadFramebuffer = apiState.getReadFramebuffer()->id();
3653 gl::FramebufferID stateDrawFramebuffer = apiState.getDrawFramebuffer()->id();
3654 if (stateDrawFramebuffer == stateReadFramebuffer)
3655 {
3656 if (currentDrawFramebuffer != stateDrawFramebuffer ||
3657 currentReadFramebuffer != stateReadFramebuffer)
3658 {
3659 cap(framebufferFuncs.bindFramebuffer(replayState, true, GL_FRAMEBUFFER,
3660 stateDrawFramebuffer));
3661 currentDrawFramebuffer = currentReadFramebuffer = stateDrawFramebuffer;
3662 }
3663 }
3664 else
3665 {
3666 if (currentDrawFramebuffer != stateDrawFramebuffer)
3667 {
3668 cap(framebufferFuncs.bindFramebuffer(replayState, true, GL_DRAW_FRAMEBUFFER,
3669 currentDrawFramebuffer));
3670 currentDrawFramebuffer = stateDrawFramebuffer;
3671 }
3672
3673 if (currentReadFramebuffer != stateReadFramebuffer)
3674 {
3675 cap(framebufferFuncs.bindFramebuffer(replayState, true, GL_READ_FRAMEBUFFER,
3676 replayState.getReadFramebuffer()->id()));
3677 currentReadFramebuffer = stateReadFramebuffer;
3678 }
3679 }
3680 }
3681
3682 // Capture Program Pipelines
3683 const gl::ProgramPipelineManager *programPipelineManager =
3684 apiState.getProgramPipelineManagerForCapture();
3685
3686 for (const auto &ppoIterator : *programPipelineManager)
3687 {
3688 gl::ProgramPipeline *pipeline = ppoIterator.second;
3689 gl::ProgramPipelineID id = {ppoIterator.first};
3690 cap(CaptureGenProgramPipelines(replayState, true, 1, &id));
3691 MaybeCaptureUpdateResourceIDs(resourceTracker, setupCalls);
3692
3693 // PPOs can contain graphics and compute programs, so loop through all shader types rather
3694 // than just the linked ones since getLinkedShaderStages() will return either only graphics
3695 // or compute stages.
3696 for (gl::ShaderType shaderType : gl::AllShaderTypes())
3697 {
3698 gl::Program *program = pipeline->getShaderProgram(shaderType);
3699 if (!program)
3700 {
3701 continue;
3702 }
3703 ASSERT(program->isLinked());
3704 GLbitfield gLbitfield = GetBitfieldFromShaderType(shaderType);
3705 cap(CaptureUseProgramStages(replayState, true, pipeline->id(), gLbitfield,
3706 program->id()));
3707
3708 // Set this program as active so it will be generated in Setup
3709 // Note: We aren't filtering ProgramPipelines, so this could be setting programs
3710 // active that aren't actually used.
3711 MarkResourceIDActive(ResourceIDType::ShaderProgram, program->id().value,
3712 shareGroupSetupCalls, resourceIDToSetupCalls);
3713 }
3714
3715 gl::Program *program = pipeline->getActiveShaderProgram();
3716 if (program)
3717 {
3718 cap(CaptureActiveShaderProgram(replayState, true, id, program->id()));
3719 }
3720 }
3721
3722 // For now we assume the installed program executable is the same as the current program.
3723 // TODO(jmadill): Handle installed program executable. http://anglebug.com/3662
3724 if (!context->isGLES1())
3725 {
3726 // If we have a program bound in the API, or if there is no program bound to the API at
3727 // time of capture and we bound a program for uniform updates during MEC, we must add
3728 // a set program call to replay the correct states.
3729 if (apiState.getProgram())
3730 {
3731 cap(CaptureUseProgram(replayState, true, apiState.getProgram()->id()));
3732 CaptureUpdateCurrentProgram(setupCalls->back(), 0, setupCalls);
3733 (void)replayState.setProgram(context, apiState.getProgram());
3734
3735 // Set this program as active so it will be generated in Setup
3736 MarkResourceIDActive(ResourceIDType::ShaderProgram, apiState.getProgram()->id().value,
3737 shareGroupSetupCalls, resourceIDToSetupCalls);
3738 }
3739 else if (replayState.getProgram())
3740 {
3741 cap(CaptureUseProgram(replayState, true, {0}));
3742 CaptureUpdateCurrentProgram(setupCalls->back(), 0, setupCalls);
3743 (void)replayState.setProgram(context, nullptr);
3744 }
3745
3746 // Same for program pipelines as for programs, see comment above.
3747 if (apiState.getProgramPipeline())
3748 {
3749 cap(CaptureBindProgramPipeline(replayState, true, apiState.getProgramPipeline()->id()));
3750 }
3751 else if (replayState.getProgramPipeline())
3752 {
3753 cap(CaptureBindProgramPipeline(replayState, true, {0}));
3754 }
3755 }
3756
3757 // Create existing queries. Note that queries may be genned and not yet started. In that
3758 // case the queries will exist in the query map as nullptr entries.
3759 const gl::QueryMap &queryMap = context->getQueriesForCapture();
3760 for (gl::QueryMap::Iterator queryIter = queryMap.beginWithNull();
3761 queryIter != queryMap.endWithNull(); ++queryIter)
3762 {
3763 ASSERT(queryIter->first);
3764 gl::QueryID queryID = {queryIter->first};
3765
3766 cap(CaptureGenQueries(replayState, true, 1, &queryID));
3767 MaybeCaptureUpdateResourceIDs(resourceTracker, setupCalls);
3768
3769 gl::Query *query = queryIter->second;
3770 if (query)
3771 {
3772 gl::QueryType queryType = query->getType();
3773
3774 // Begin the query to generate the object
3775 cap(CaptureBeginQuery(replayState, true, queryType, queryID));
3776
3777 // End the query if it was not active
3778 if (!IsQueryActive(apiState, queryID))
3779 {
3780 cap(CaptureEndQuery(replayState, true, queryType));
3781 }
3782 }
3783 }
3784
3785 // Transform Feedback
3786 const gl::TransformFeedbackMap &xfbMap = context->getTransformFeedbacksForCapture();
3787 for (const auto &xfbIter : xfbMap)
3788 {
3789 gl::TransformFeedbackID xfbID = {xfbIter.first};
3790
3791 // Do not capture the default XFB object.
3792 if (xfbID.value == 0)
3793 {
3794 continue;
3795 }
3796
3797 cap(CaptureGenTransformFeedbacks(replayState, true, 1, &xfbID));
3798 MaybeCaptureUpdateResourceIDs(resourceTracker, setupCalls);
3799
3800 gl::TransformFeedback *xfb = xfbIter.second;
3801 if (!xfb)
3802 {
3803 // The object was never created
3804 continue;
3805 }
3806
3807 // Bind XFB to create the object
3808 cap(CaptureBindTransformFeedback(replayState, true, GL_TRANSFORM_FEEDBACK, xfbID));
3809
3810 // Bind the buffers associated with this XFB object
3811 for (size_t i = 0; i < xfb->getIndexedBufferCount(); ++i)
3812 {
3813 const gl::OffsetBindingPointer<gl::Buffer> &xfbBuffer = xfb->getIndexedBuffer(i);
3814
3815 // Note: Buffers bound with BindBufferBase can be used with BindBuffer
3816 cap(CaptureBindBufferRange(replayState, true, gl::BufferBinding::TransformFeedback, 0,
3817 xfbBuffer.id(), xfbBuffer.getOffset(), xfbBuffer.getSize()));
3818 }
3819
3820 if (xfb->isActive() || xfb->isPaused())
3821 {
3822 // We don't support active XFB in MEC yet
3823 UNIMPLEMENTED();
3824 }
3825 }
3826
3827 // Bind the current XFB buffer after populating XFB objects
3828 gl::TransformFeedback *currentXFB = apiState.getCurrentTransformFeedback();
3829 if (currentXFB)
3830 {
3831 cap(CaptureBindTransformFeedback(replayState, true, GL_TRANSFORM_FEEDBACK,
3832 currentXFB->id()));
3833 }
3834
3835 // Bind samplers
3836 const gl::SamplerBindingVector &samplerBindings = apiState.getSamplers();
3837 for (GLuint bindingIndex = 0; bindingIndex < static_cast<GLuint>(samplerBindings.size());
3838 ++bindingIndex)
3839 {
3840 gl::SamplerID samplerID = samplerBindings[bindingIndex].id();
3841 if (samplerID.value != 0)
3842 {
3843 cap(CaptureBindSampler(replayState, true, bindingIndex, samplerID));
3844 }
3845 }
3846
3847 // Capture Image Texture bindings
3848 const std::vector<gl::ImageUnit> &imageUnits = apiState.getImageUnits();
3849 for (GLuint bindingIndex = 0; bindingIndex < static_cast<GLuint>(imageUnits.size());
3850 ++bindingIndex)
3851 {
3852 const gl::ImageUnit &imageUnit = imageUnits[bindingIndex];
3853
3854 if (imageUnit.texture == 0)
3855 {
3856 continue;
3857 }
3858
3859 cap(CaptureBindImageTexture(replayState, true, bindingIndex, imageUnit.texture.id(),
3860 imageUnit.level, imageUnit.layered, imageUnit.layer,
3861 imageUnit.access, imageUnit.format));
3862 }
3863
3864 // Capture GL Context states.
3865 auto capCap = [cap, &replayState](GLenum capEnum, bool capValue) {
3866 if (capValue)
3867 {
3868 cap(CaptureEnable(replayState, true, capEnum));
3869 }
3870 else
3871 {
3872 cap(CaptureDisable(replayState, true, capEnum));
3873 }
3874 };
3875
3876 // Capture GLES1 context states.
3877 if (context->isGLES1())
3878 {
3879 const bool currentTextureState = apiState.getEnableFeature(GL_TEXTURE_2D);
3880 const bool defaultTextureState = replayState.getEnableFeature(GL_TEXTURE_2D);
3881 if (currentTextureState != defaultTextureState)
3882 {
3883 capCap(GL_TEXTURE_2D, currentTextureState);
3884 }
3885
3886 cap(CaptureMatrixMode(replayState, true, gl::MatrixType::Projection));
3887 for (angle::Mat4 projectionMatrix :
3888 apiState.gles1().getMatrixStack(gl::MatrixType::Projection))
3889 {
3890 cap(CapturePushMatrix(replayState, true));
3891 cap(CaptureLoadMatrixf(replayState, true, projectionMatrix.elements().data()));
3892 }
3893
3894 cap(CaptureMatrixMode(replayState, true, gl::MatrixType::Modelview));
3895 for (angle::Mat4 modelViewMatrix :
3896 apiState.gles1().getMatrixStack(gl::MatrixType::Modelview))
3897 {
3898 cap(CapturePushMatrix(replayState, true));
3899 cap(CaptureLoadMatrixf(replayState, true, modelViewMatrix.elements().data()));
3900 }
3901
3902 gl::MatrixType currentMatrixMode = apiState.gles1().getMatrixMode();
3903 if (currentMatrixMode != gl::MatrixType::Modelview)
3904 {
3905 cap(CaptureMatrixMode(replayState, true, currentMatrixMode));
3906 }
3907 }
3908
3909 // Rasterizer state. Missing ES 3.x features.
3910 const gl::RasterizerState &defaultRasterState = replayState.getRasterizerState();
3911 const gl::RasterizerState ¤tRasterState = apiState.getRasterizerState();
3912 if (currentRasterState.cullFace != defaultRasterState.cullFace)
3913 {
3914 capCap(GL_CULL_FACE, currentRasterState.cullFace);
3915 }
3916
3917 if (currentRasterState.cullMode != defaultRasterState.cullMode)
3918 {
3919 cap(CaptureCullFace(replayState, true, currentRasterState.cullMode));
3920 }
3921
3922 if (currentRasterState.frontFace != defaultRasterState.frontFace)
3923 {
3924 cap(CaptureFrontFace(replayState, true, currentRasterState.frontFace));
3925 }
3926
3927 if (currentRasterState.polygonOffsetFill != defaultRasterState.polygonOffsetFill)
3928 {
3929 capCap(GL_POLYGON_OFFSET_FILL, currentRasterState.polygonOffsetFill);
3930 }
3931
3932 if (currentRasterState.polygonOffsetFactor != defaultRasterState.polygonOffsetFactor ||
3933 currentRasterState.polygonOffsetUnits != defaultRasterState.polygonOffsetUnits)
3934 {
3935 cap(CapturePolygonOffset(replayState, true, currentRasterState.polygonOffsetFactor,
3936 currentRasterState.polygonOffsetUnits));
3937 }
3938
3939 // pointDrawMode/multiSample are only used in the D3D back-end right now.
3940
3941 if (currentRasterState.rasterizerDiscard != defaultRasterState.rasterizerDiscard)
3942 {
3943 capCap(GL_RASTERIZER_DISCARD, currentRasterState.rasterizerDiscard);
3944 }
3945
3946 if (currentRasterState.dither != defaultRasterState.dither)
3947 {
3948 capCap(GL_DITHER, currentRasterState.dither);
3949 }
3950
3951 // Depth/stencil state.
3952 const gl::DepthStencilState &defaultDSState = replayState.getDepthStencilState();
3953 const gl::DepthStencilState ¤tDSState = apiState.getDepthStencilState();
3954 if (defaultDSState.depthFunc != currentDSState.depthFunc)
3955 {
3956 cap(CaptureDepthFunc(replayState, true, currentDSState.depthFunc));
3957 }
3958
3959 if (defaultDSState.depthMask != currentDSState.depthMask)
3960 {
3961 cap(CaptureDepthMask(replayState, true, gl::ConvertToGLBoolean(currentDSState.depthMask)));
3962 }
3963
3964 if (defaultDSState.depthTest != currentDSState.depthTest)
3965 {
3966 capCap(GL_DEPTH_TEST, currentDSState.depthTest);
3967 }
3968
3969 if (defaultDSState.stencilTest != currentDSState.stencilTest)
3970 {
3971 capCap(GL_STENCIL_TEST, currentDSState.stencilTest);
3972 }
3973
3974 if (currentDSState.stencilFunc == currentDSState.stencilBackFunc &&
3975 currentDSState.stencilMask == currentDSState.stencilBackMask)
3976 {
3977 // Front and back are equal
3978 if (defaultDSState.stencilFunc != currentDSState.stencilFunc ||
3979 defaultDSState.stencilMask != currentDSState.stencilMask ||
3980 apiState.getStencilRef() != 0)
3981 {
3982 cap(CaptureStencilFunc(replayState, true, currentDSState.stencilFunc,
3983 apiState.getStencilRef(), currentDSState.stencilMask));
3984 }
3985 }
3986 else
3987 {
3988 // Front and back are separate
3989 if (defaultDSState.stencilFunc != currentDSState.stencilFunc ||
3990 defaultDSState.stencilMask != currentDSState.stencilMask ||
3991 apiState.getStencilRef() != 0)
3992 {
3993 cap(CaptureStencilFuncSeparate(replayState, true, GL_FRONT, currentDSState.stencilFunc,
3994 apiState.getStencilRef(), currentDSState.stencilMask));
3995 }
3996
3997 if (defaultDSState.stencilBackFunc != currentDSState.stencilBackFunc ||
3998 defaultDSState.stencilBackMask != currentDSState.stencilBackMask ||
3999 apiState.getStencilBackRef() != 0)
4000 {
4001 cap(CaptureStencilFuncSeparate(
4002 replayState, true, GL_BACK, currentDSState.stencilBackFunc,
4003 apiState.getStencilBackRef(), currentDSState.stencilBackMask));
4004 }
4005 }
4006
4007 if (currentDSState.stencilFail == currentDSState.stencilBackFail &&
4008 currentDSState.stencilPassDepthFail == currentDSState.stencilBackPassDepthFail &&
4009 currentDSState.stencilPassDepthPass == currentDSState.stencilBackPassDepthPass)
4010 {
4011 // Front and back are equal
4012 if (defaultDSState.stencilFail != currentDSState.stencilFail ||
4013 defaultDSState.stencilPassDepthFail != currentDSState.stencilPassDepthFail ||
4014 defaultDSState.stencilPassDepthPass != currentDSState.stencilPassDepthPass)
4015 {
4016 cap(CaptureStencilOp(replayState, true, currentDSState.stencilFail,
4017 currentDSState.stencilPassDepthFail,
4018 currentDSState.stencilPassDepthPass));
4019 }
4020 }
4021 else
4022 {
4023 // Front and back are separate
4024 if (defaultDSState.stencilFail != currentDSState.stencilFail ||
4025 defaultDSState.stencilPassDepthFail != currentDSState.stencilPassDepthFail ||
4026 defaultDSState.stencilPassDepthPass != currentDSState.stencilPassDepthPass)
4027 {
4028 cap(CaptureStencilOpSeparate(replayState, true, GL_FRONT, currentDSState.stencilFail,
4029 currentDSState.stencilPassDepthFail,
4030 currentDSState.stencilPassDepthPass));
4031 }
4032
4033 if (defaultDSState.stencilBackFail != currentDSState.stencilBackFail ||
4034 defaultDSState.stencilBackPassDepthFail != currentDSState.stencilBackPassDepthFail ||
4035 defaultDSState.stencilBackPassDepthPass != currentDSState.stencilBackPassDepthPass)
4036 {
4037 cap(CaptureStencilOpSeparate(replayState, true, GL_BACK, currentDSState.stencilBackFail,
4038 currentDSState.stencilBackPassDepthFail,
4039 currentDSState.stencilBackPassDepthPass));
4040 }
4041 }
4042
4043 if (currentDSState.stencilWritemask == currentDSState.stencilBackWritemask)
4044 {
4045 // Front and back are equal
4046 if (defaultDSState.stencilWritemask != currentDSState.stencilWritemask)
4047 {
4048 cap(CaptureStencilMask(replayState, true, currentDSState.stencilWritemask));
4049 }
4050 }
4051 else
4052 {
4053 // Front and back are separate
4054 if (defaultDSState.stencilWritemask != currentDSState.stencilWritemask)
4055 {
4056 cap(CaptureStencilMaskSeparate(replayState, true, GL_FRONT,
4057 currentDSState.stencilWritemask));
4058 }
4059
4060 if (defaultDSState.stencilBackWritemask != currentDSState.stencilBackWritemask)
4061 {
4062 cap(CaptureStencilMaskSeparate(replayState, true, GL_BACK,
4063 currentDSState.stencilBackWritemask));
4064 }
4065 }
4066
4067 // Blend state.
4068 const gl::BlendState &defaultBlendState = replayState.getBlendState();
4069 const gl::BlendState ¤tBlendState = apiState.getBlendState();
4070
4071 if (currentBlendState.blend != defaultBlendState.blend)
4072 {
4073 capCap(GL_BLEND, currentBlendState.blend);
4074 }
4075
4076 if (currentBlendState.sourceBlendRGB != defaultBlendState.sourceBlendRGB ||
4077 currentBlendState.destBlendRGB != defaultBlendState.destBlendRGB ||
4078 currentBlendState.sourceBlendAlpha != defaultBlendState.sourceBlendAlpha ||
4079 currentBlendState.destBlendAlpha != defaultBlendState.destBlendAlpha)
4080 {
4081 if (currentBlendState.sourceBlendRGB == currentBlendState.sourceBlendAlpha &&
4082 currentBlendState.destBlendRGB == currentBlendState.destBlendAlpha)
4083 {
4084 // Color and alpha are equal
4085 cap(CaptureBlendFunc(replayState, true, currentBlendState.sourceBlendRGB,
4086 currentBlendState.destBlendRGB));
4087 }
4088 else
4089 {
4090 // Color and alpha are separate
4091 cap(CaptureBlendFuncSeparate(
4092 replayState, true, currentBlendState.sourceBlendRGB, currentBlendState.destBlendRGB,
4093 currentBlendState.sourceBlendAlpha, currentBlendState.destBlendAlpha));
4094 }
4095 }
4096
4097 if (currentBlendState.blendEquationRGB != defaultBlendState.blendEquationRGB ||
4098 currentBlendState.blendEquationAlpha != defaultBlendState.blendEquationAlpha)
4099 {
4100 cap(CaptureBlendEquationSeparate(replayState, true, currentBlendState.blendEquationRGB,
4101 currentBlendState.blendEquationAlpha));
4102 }
4103
4104 if (currentBlendState.colorMaskRed != defaultBlendState.colorMaskRed ||
4105 currentBlendState.colorMaskGreen != defaultBlendState.colorMaskGreen ||
4106 currentBlendState.colorMaskBlue != defaultBlendState.colorMaskBlue ||
4107 currentBlendState.colorMaskAlpha != defaultBlendState.colorMaskAlpha)
4108 {
4109 cap(CaptureColorMask(replayState, true,
4110 gl::ConvertToGLBoolean(currentBlendState.colorMaskRed),
4111 gl::ConvertToGLBoolean(currentBlendState.colorMaskGreen),
4112 gl::ConvertToGLBoolean(currentBlendState.colorMaskBlue),
4113 gl::ConvertToGLBoolean(currentBlendState.colorMaskAlpha)));
4114 }
4115
4116 const gl::ColorF ¤tBlendColor = apiState.getBlendColor();
4117 if (currentBlendColor != gl::ColorF())
4118 {
4119 cap(CaptureBlendColor(replayState, true, currentBlendColor.red, currentBlendColor.green,
4120 currentBlendColor.blue, currentBlendColor.alpha));
4121 }
4122
4123 // Pixel storage states.
4124 gl::PixelPackState ¤tPackState = replayState.getPackState();
4125 if (currentPackState.alignment != apiState.getPackAlignment())
4126 {
4127 cap(CapturePixelStorei(replayState, true, GL_PACK_ALIGNMENT, apiState.getPackAlignment()));
4128 currentPackState.alignment = apiState.getPackAlignment();
4129 }
4130
4131 if (currentPackState.rowLength != apiState.getPackRowLength())
4132 {
4133 cap(CapturePixelStorei(replayState, true, GL_PACK_ROW_LENGTH, apiState.getPackRowLength()));
4134 currentPackState.rowLength = apiState.getPackRowLength();
4135 }
4136
4137 if (currentPackState.skipRows != apiState.getPackSkipRows())
4138 {
4139 cap(CapturePixelStorei(replayState, true, GL_PACK_SKIP_ROWS, apiState.getPackSkipRows()));
4140 currentPackState.skipRows = apiState.getPackSkipRows();
4141 }
4142
4143 if (currentPackState.skipPixels != apiState.getPackSkipPixels())
4144 {
4145 cap(CapturePixelStorei(replayState, true, GL_PACK_SKIP_PIXELS,
4146 apiState.getPackSkipPixels()));
4147 currentPackState.skipPixels = apiState.getPackSkipPixels();
4148 }
4149
4150 // We set unpack alignment above, no need to change it here
4151 ASSERT(currentUnpackState.alignment == 1);
4152 if (currentUnpackState.rowLength != apiState.getUnpackRowLength())
4153 {
4154 cap(CapturePixelStorei(replayState, true, GL_UNPACK_ROW_LENGTH,
4155 apiState.getUnpackRowLength()));
4156 currentUnpackState.rowLength = apiState.getUnpackRowLength();
4157 }
4158
4159 if (currentUnpackState.skipRows != apiState.getUnpackSkipRows())
4160 {
4161 cap(CapturePixelStorei(replayState, true, GL_UNPACK_SKIP_ROWS,
4162 apiState.getUnpackSkipRows()));
4163 currentUnpackState.skipRows = apiState.getUnpackSkipRows();
4164 }
4165
4166 if (currentUnpackState.skipPixels != apiState.getUnpackSkipPixels())
4167 {
4168 cap(CapturePixelStorei(replayState, true, GL_UNPACK_SKIP_PIXELS,
4169 apiState.getUnpackSkipPixels()));
4170 currentUnpackState.skipPixels = apiState.getUnpackSkipPixels();
4171 }
4172
4173 if (currentUnpackState.imageHeight != apiState.getUnpackImageHeight())
4174 {
4175 cap(CapturePixelStorei(replayState, true, GL_UNPACK_IMAGE_HEIGHT,
4176 apiState.getUnpackImageHeight()));
4177 currentUnpackState.imageHeight = apiState.getUnpackImageHeight();
4178 }
4179
4180 if (currentUnpackState.skipImages != apiState.getUnpackSkipImages())
4181 {
4182 cap(CapturePixelStorei(replayState, true, GL_UNPACK_SKIP_IMAGES,
4183 apiState.getUnpackSkipImages()));
4184 currentUnpackState.skipImages = apiState.getUnpackSkipImages();
4185 }
4186
4187 // Clear state. Missing ES 3.x features.
4188 // TODO(http://anglebug.com/3662): Complete state capture.
4189 const gl::ColorF ¤tClearColor = apiState.getColorClearValue();
4190 if (currentClearColor != gl::ColorF())
4191 {
4192 cap(CaptureClearColor(replayState, true, currentClearColor.red, currentClearColor.green,
4193 currentClearColor.blue, currentClearColor.alpha));
4194 }
4195
4196 if (apiState.getDepthClearValue() != 1.0f)
4197 {
4198 cap(CaptureClearDepthf(replayState, true, apiState.getDepthClearValue()));
4199 }
4200
4201 if (apiState.getStencilClearValue() != 0)
4202 {
4203 cap(CaptureClearStencil(replayState, true, apiState.getStencilClearValue()));
4204 }
4205
4206 // Viewport / scissor / clipping planes.
4207 const gl::Rectangle ¤tViewport = apiState.getViewport();
4208 if (currentViewport != gl::Rectangle())
4209 {
4210 cap(CaptureViewport(replayState, true, currentViewport.x, currentViewport.y,
4211 currentViewport.width, currentViewport.height));
4212 }
4213
4214 if (apiState.getNearPlane() != 0.0f || apiState.getFarPlane() != 1.0f)
4215 {
4216 cap(CaptureDepthRangef(replayState, true, apiState.getNearPlane(), apiState.getFarPlane()));
4217 }
4218
4219 if (apiState.isScissorTestEnabled())
4220 {
4221 capCap(GL_SCISSOR_TEST, apiState.isScissorTestEnabled());
4222 }
4223
4224 const gl::Rectangle ¤tScissor = apiState.getScissor();
4225 if (currentScissor != gl::Rectangle())
4226 {
4227 cap(CaptureScissor(replayState, true, currentScissor.x, currentScissor.y,
4228 currentScissor.width, currentScissor.height));
4229 }
4230
4231 // Allow the replayState object to be destroyed conveniently.
4232 replayState.setBufferBinding(context, gl::BufferBinding::Array, nullptr);
4233
4234 // Clean up the replay state.
4235 replayState.reset(context);
4236
4237 if (validationEnabled)
4238 {
4239 CaptureValidateSerializedState(context, setupCalls);
4240 }
4241 }
4242
SkipCall(EntryPoint entryPoint)4243 bool SkipCall(EntryPoint entryPoint)
4244 {
4245 switch (entryPoint)
4246 {
4247 case EntryPoint::GLDebugMessageCallback:
4248 case EntryPoint::GLDebugMessageCallbackKHR:
4249 case EntryPoint::GLDebugMessageControl:
4250 case EntryPoint::GLDebugMessageControlKHR:
4251 case EntryPoint::GLDebugMessageInsert:
4252 case EntryPoint::GLDebugMessageInsertKHR:
4253 case EntryPoint::GLGetDebugMessageLog:
4254 case EntryPoint::GLGetDebugMessageLogKHR:
4255 case EntryPoint::GLGetObjectLabelEXT:
4256 case EntryPoint::GLGetObjectLabelKHR:
4257 case EntryPoint::GLGetObjectPtrLabelKHR:
4258 case EntryPoint::GLGetPointervKHR:
4259 case EntryPoint::GLInsertEventMarkerEXT:
4260 case EntryPoint::GLLabelObjectEXT:
4261 case EntryPoint::GLObjectLabelKHR:
4262 case EntryPoint::GLObjectPtrLabelKHR:
4263 case EntryPoint::GLPopDebugGroupKHR:
4264 case EntryPoint::GLPopGroupMarkerEXT:
4265 case EntryPoint::GLPushDebugGroupKHR:
4266 case EntryPoint::GLPushGroupMarkerEXT:
4267 // Purposefully skip entry points from:
4268 // - KHR_debug
4269 // - EXT_debug_label
4270 // - EXT_debug_marker
4271 // There is no need to capture these for replaying a trace in our harness
4272 return true;
4273
4274 case EntryPoint::GLGetActiveUniform:
4275 case EntryPoint::GLGetActiveUniformsiv:
4276 // Skip these calls because:
4277 // - We don't use the return values.
4278 // - Active uniform counts can vary between platforms due to cross stage optimizations
4279 // and asking about uniforms above GL_ACTIVE_UNIFORMS triggers errors.
4280 return true;
4281
4282 default:
4283 break;
4284 }
4285
4286 return false;
4287 }
4288
4289 template <typename ParamValueType>
4290 struct ParamValueTrait
4291 {
4292 static_assert(sizeof(ParamValueType) == 0, "invalid ParamValueType");
4293 };
4294
4295 template <>
4296 struct ParamValueTrait<gl::FramebufferID>
4297 {
4298 static constexpr const char *name = "framebufferPacked";
4299 static const ParamType typeID = ParamType::TFramebufferID;
4300 };
4301
GetBaseName(const std::string & nameWithPath)4302 std::string GetBaseName(const std::string &nameWithPath)
4303 {
4304 std::vector<std::string> result = angle::SplitString(
4305 nameWithPath, "/\\", WhitespaceHandling::TRIM_WHITESPACE, SplitResult::SPLIT_WANT_NONEMPTY);
4306 ASSERT(!result.empty());
4307 return result.back();
4308 }
4309
4310 template <>
4311 struct ParamValueTrait<gl::BufferID>
4312 {
4313 static constexpr const char *name = "bufferPacked";
4314 static const ParamType typeID = ParamType::TBufferID;
4315 };
4316
4317 template <>
4318 struct ParamValueTrait<gl::RenderbufferID>
4319 {
4320 static constexpr const char *name = "renderbufferPacked";
4321 static const ParamType typeID = ParamType::TRenderbufferID;
4322 };
4323
4324 template <>
4325 struct ParamValueTrait<gl::TextureID>
4326 {
4327 static constexpr const char *name = "texturePacked";
4328 static const ParamType typeID = ParamType::TTextureID;
4329 };
4330
4331 } // namespace
4332
ParamCapture()4333 ParamCapture::ParamCapture() : type(ParamType::TGLenum), enumGroup(gl::GLenumGroup::DefaultGroup) {}
4334
ParamCapture(const char * nameIn,ParamType typeIn)4335 ParamCapture::ParamCapture(const char *nameIn, ParamType typeIn)
4336 : name(nameIn), type(typeIn), enumGroup(gl::GLenumGroup::DefaultGroup)
4337 {}
4338
4339 ParamCapture::~ParamCapture() = default;
4340
ParamCapture(ParamCapture && other)4341 ParamCapture::ParamCapture(ParamCapture &&other)
4342 : type(ParamType::TGLenum), enumGroup(gl::GLenumGroup::DefaultGroup)
4343 {
4344 *this = std::move(other);
4345 }
4346
operator =(ParamCapture && other)4347 ParamCapture &ParamCapture::operator=(ParamCapture &&other)
4348 {
4349 std::swap(name, other.name);
4350 std::swap(type, other.type);
4351 std::swap(value, other.value);
4352 std::swap(enumGroup, other.enumGroup);
4353 std::swap(data, other.data);
4354 std::swap(arrayClientPointerIndex, other.arrayClientPointerIndex);
4355 std::swap(readBufferSizeBytes, other.readBufferSizeBytes);
4356 std::swap(dataNElements, other.dataNElements);
4357 return *this;
4358 }
4359
ParamBuffer()4360 ParamBuffer::ParamBuffer() {}
4361
4362 ParamBuffer::~ParamBuffer() = default;
4363
ParamBuffer(ParamBuffer && other)4364 ParamBuffer::ParamBuffer(ParamBuffer &&other)
4365 {
4366 *this = std::move(other);
4367 }
4368
operator =(ParamBuffer && other)4369 ParamBuffer &ParamBuffer::operator=(ParamBuffer &&other)
4370 {
4371 std::swap(mParamCaptures, other.mParamCaptures);
4372 std::swap(mClientArrayDataParam, other.mClientArrayDataParam);
4373 std::swap(mReadBufferSize, other.mReadBufferSize);
4374 std::swap(mReturnValueCapture, other.mReturnValueCapture);
4375 std::swap(mMappedBufferID, other.mMappedBufferID);
4376 return *this;
4377 }
4378
getParam(const char * paramName,ParamType paramType,int index)4379 ParamCapture &ParamBuffer::getParam(const char *paramName, ParamType paramType, int index)
4380 {
4381 ParamCapture &capture = mParamCaptures[index];
4382 ASSERT(capture.name == paramName);
4383 ASSERT(capture.type == paramType);
4384 return capture;
4385 }
4386
getParam(const char * paramName,ParamType paramType,int index) const4387 const ParamCapture &ParamBuffer::getParam(const char *paramName,
4388 ParamType paramType,
4389 int index) const
4390 {
4391 return const_cast<ParamBuffer *>(this)->getParam(paramName, paramType, index);
4392 }
4393
getParamFlexName(const char * paramName1,const char * paramName2,ParamType paramType,int index)4394 ParamCapture &ParamBuffer::getParamFlexName(const char *paramName1,
4395 const char *paramName2,
4396 ParamType paramType,
4397 int index)
4398 {
4399 ParamCapture &capture = mParamCaptures[index];
4400 ASSERT(capture.name == paramName1 || capture.name == paramName2);
4401 ASSERT(capture.type == paramType);
4402 return capture;
4403 }
4404
getParamFlexName(const char * paramName1,const char * paramName2,ParamType paramType,int index) const4405 const ParamCapture &ParamBuffer::getParamFlexName(const char *paramName1,
4406 const char *paramName2,
4407 ParamType paramType,
4408 int index) const
4409 {
4410 return const_cast<ParamBuffer *>(this)->getParamFlexName(paramName1, paramName2, paramType,
4411 index);
4412 }
4413
addParam(ParamCapture && param)4414 void ParamBuffer::addParam(ParamCapture &¶m)
4415 {
4416 if (param.arrayClientPointerIndex != -1)
4417 {
4418 ASSERT(mClientArrayDataParam == -1);
4419 mClientArrayDataParam = static_cast<int>(mParamCaptures.size());
4420 }
4421
4422 mReadBufferSize = std::max(param.readBufferSizeBytes, mReadBufferSize);
4423 mParamCaptures.emplace_back(std::move(param));
4424 }
4425
addReturnValue(ParamCapture && returnValue)4426 void ParamBuffer::addReturnValue(ParamCapture &&returnValue)
4427 {
4428 mReturnValueCapture = std::move(returnValue);
4429 }
4430
getClientArrayPointerParameter()4431 ParamCapture &ParamBuffer::getClientArrayPointerParameter()
4432 {
4433 ASSERT(hasClientArrayData());
4434 return mParamCaptures[mClientArrayDataParam];
4435 }
4436
CallCapture(EntryPoint entryPointIn,ParamBuffer && paramsIn)4437 CallCapture::CallCapture(EntryPoint entryPointIn, ParamBuffer &¶msIn)
4438 : entryPoint(entryPointIn), params(std::move(paramsIn))
4439 {}
4440
CallCapture(const std::string & customFunctionNameIn,ParamBuffer && paramsIn)4441 CallCapture::CallCapture(const std::string &customFunctionNameIn, ParamBuffer &¶msIn)
4442 : entryPoint(EntryPoint::GLInvalid),
4443 customFunctionName(customFunctionNameIn),
4444 params(std::move(paramsIn))
4445 {}
4446
4447 CallCapture::~CallCapture() = default;
4448
CallCapture(CallCapture && other)4449 CallCapture::CallCapture(CallCapture &&other)
4450 {
4451 *this = std::move(other);
4452 }
4453
operator =(CallCapture && other)4454 CallCapture &CallCapture::operator=(CallCapture &&other)
4455 {
4456 std::swap(entryPoint, other.entryPoint);
4457 std::swap(customFunctionName, other.customFunctionName);
4458 std::swap(params, other.params);
4459 std::swap(isActive, other.isActive);
4460 return *this;
4461 }
4462
name() const4463 const char *CallCapture::name() const
4464 {
4465 if (entryPoint == EntryPoint::GLInvalid)
4466 {
4467 ASSERT(!customFunctionName.empty());
4468 return customFunctionName.c_str();
4469 }
4470
4471 return angle::GetEntryPointName(entryPoint);
4472 }
4473
ReplayContext(size_t readBufferSizebytes,const gl::AttribArray<size_t> & clientArraysSizebytes)4474 ReplayContext::ReplayContext(size_t readBufferSizebytes,
4475 const gl::AttribArray<size_t> &clientArraysSizebytes)
4476 {
4477 mReadBuffer.resize(readBufferSizebytes);
4478
4479 for (uint32_t i = 0; i < clientArraysSizebytes.size(); i++)
4480 {
4481 mClientArraysBuffer[i].resize(clientArraysSizebytes[i]);
4482 }
4483 }
~ReplayContext()4484 ReplayContext::~ReplayContext() {}
4485
4486 FrameCapture::FrameCapture() = default;
4487 FrameCapture::~FrameCapture() = default;
4488
reset()4489 void FrameCapture::reset()
4490 {
4491 mSetupCalls.clear();
4492 }
4493
FrameCaptureShared()4494 FrameCaptureShared::FrameCaptureShared()
4495 : mLastContextId{0},
4496 mEnabled(true),
4497 mSerializeStateEnabled(false),
4498 mCompression(true),
4499 mClientVertexArrayMap{},
4500 mFrameIndex(1),
4501 mCaptureStartFrame(1),
4502 mCaptureEndFrame(0),
4503 mClientArraySizes{},
4504 mReadBufferSize(0),
4505 mHasResourceType{},
4506 mResourceIDToSetupCalls{},
4507 mMaxAccessedResourceIDs{},
4508 mCaptureTrigger(0),
4509 mCaptureActive(false),
4510 mMidExecutionCaptureActive(false),
4511 mWindowSurfaceContextID({0})
4512 {
4513 reset();
4514
4515 std::string enabledFromEnv =
4516 GetEnvironmentVarOrUnCachedAndroidProperty(kEnabledVarName, kAndroidEnabled);
4517 if (enabledFromEnv == "0")
4518 {
4519 mEnabled = false;
4520 }
4521
4522 std::string pathFromEnv =
4523 GetEnvironmentVarOrUnCachedAndroidProperty(kOutDirectoryVarName, kAndroidOutDir);
4524 if (pathFromEnv.empty())
4525 {
4526 mOutDirectory = GetDefaultOutDirectory();
4527 }
4528 else
4529 {
4530 mOutDirectory = pathFromEnv;
4531 }
4532
4533 // Ensure the capture path ends with a slash.
4534 if (mOutDirectory.back() != '\\' && mOutDirectory.back() != '/')
4535 {
4536 mOutDirectory += '/';
4537 }
4538
4539 std::string startFromEnv =
4540 GetEnvironmentVarOrUnCachedAndroidProperty(kFrameStartVarName, kAndroidFrameStart);
4541 if (!startFromEnv.empty())
4542 {
4543 mCaptureStartFrame = atoi(startFromEnv.c_str());
4544 }
4545 if (mCaptureStartFrame < 1)
4546 {
4547 WARN() << "Cannot use a capture start frame less than 1.";
4548 mCaptureStartFrame = 1;
4549 }
4550
4551 std::string endFromEnv =
4552 GetEnvironmentVarOrUnCachedAndroidProperty(kFrameEndVarName, kAndroidFrameEnd);
4553 if (!endFromEnv.empty())
4554 {
4555 mCaptureEndFrame = atoi(endFromEnv.c_str());
4556 }
4557
4558 std::string captureTriggerFromEnv =
4559 GetEnvironmentVarOrUnCachedAndroidProperty(kTriggerVarName, kAndroidTrigger);
4560 if (!captureTriggerFromEnv.empty())
4561 {
4562 mCaptureTrigger = atoi(captureTriggerFromEnv.c_str());
4563
4564 // If the trigger has been populated, ignore the other frame range variables by setting them
4565 // to unreasonable values. This isn't perfect, but it is effective.
4566 mCaptureStartFrame = mCaptureEndFrame = std::numeric_limits<uint32_t>::max();
4567 INFO() << "Capture trigger detected, disabling capture start/end frame.";
4568 }
4569
4570 std::string labelFromEnv =
4571 GetEnvironmentVarOrUnCachedAndroidProperty(kCaptureLabelVarName, kAndroidCaptureLabel);
4572 if (!labelFromEnv.empty())
4573 {
4574 // Optional label to provide unique file names and namespaces
4575 mCaptureLabel = labelFromEnv;
4576 }
4577
4578 std::string compressionFromEnv =
4579 GetEnvironmentVarOrUnCachedAndroidProperty(kCompressionVarName, kAndroidCompression);
4580 if (compressionFromEnv == "0")
4581 {
4582 mCompression = false;
4583 }
4584 std::string serializeStateFromEnv = angle::GetEnvironmentVar(kSerializeStateVarName);
4585 if (serializeStateFromEnv == "1")
4586 {
4587 mSerializeStateEnabled = true;
4588 }
4589
4590 std::string validateSerialiedStateFromEnv =
4591 GetEnvironmentVarOrUnCachedAndroidProperty(kValidationVarName, kAndroidValidation);
4592 if (validateSerialiedStateFromEnv == "1")
4593 {
4594 mValidateSerializedState = true;
4595 }
4596
4597 mValidationExpression =
4598 GetEnvironmentVarOrUnCachedAndroidProperty(kValidationExprVarName, kAndroidValidationExpr);
4599
4600 if (!mValidationExpression.empty())
4601 {
4602 INFO() << "Validation expression is " << kValidationExprVarName;
4603 }
4604
4605 std::string trimEnabledFromEnv =
4606 GetEnvironmentVarOrUnCachedAndroidProperty(kTrimEnabledVarName, kAndroidTrimEnabled);
4607 if (trimEnabledFromEnv == "0")
4608 {
4609 mTrimEnabled = false;
4610 }
4611
4612 std::string sourceSizeFromEnv =
4613 GetEnvironmentVarOrUnCachedAndroidProperty(kSourceSizeVarName, kAndroidSourceSize);
4614 if (!sourceSizeFromEnv.empty())
4615 {
4616 int sourceSize = atoi(sourceSizeFromEnv.c_str());
4617 if (sourceSize < 0)
4618 {
4619 WARN() << "Invalid capture source size: " << sourceSize;
4620 }
4621 else
4622 {
4623 mReplayWriter.setSourceFileSizeThreshold(sourceSize);
4624 }
4625 }
4626
4627 if (mFrameIndex == mCaptureStartFrame)
4628 {
4629 // Capture is starting from the first frame, so set the capture active to ensure all GLES
4630 // commands issued are handled correctly by maybeCapturePreCallUpdates() and
4631 // maybeCapturePostCallUpdates().
4632 setCaptureActive();
4633 }
4634
4635 if (mCaptureEndFrame < mCaptureStartFrame)
4636 {
4637 mEnabled = false;
4638 }
4639
4640 mReplayWriter.setCaptureLabel(mCaptureLabel);
4641 }
4642
4643 FrameCaptureShared::~FrameCaptureShared() = default;
4644
PageRange(size_t start,size_t end)4645 PageRange::PageRange(size_t start, size_t end) : start(start), end(end) {}
4646 PageRange::~PageRange() = default;
4647
AddressRange()4648 AddressRange::AddressRange() {}
AddressRange(uintptr_t start,size_t size)4649 AddressRange::AddressRange(uintptr_t start, size_t size) : start(start), size(size) {}
4650 AddressRange::~AddressRange() = default;
4651
end()4652 uintptr_t AddressRange::end()
4653 {
4654 return start + size;
4655 }
4656
CoherentBuffer(uintptr_t start,size_t size,size_t pageSize)4657 CoherentBuffer::CoherentBuffer(uintptr_t start, size_t size, size_t pageSize) : mPageSize(pageSize)
4658 {
4659 mRange.start = start;
4660 mRange.size = size;
4661 mProtectionRange.start = rx::roundDownPow2(start, pageSize);
4662 uintptr_t protectionEnd = rx::roundUpPow2(start + size, pageSize);
4663
4664 mProtectionRange.size = protectionEnd - mProtectionRange.start;
4665 mPageCount = mProtectionRange.size / pageSize;
4666
4667 mProtectionStartPage = mProtectionRange.start / mPageSize;
4668 mProtectionEndPage = mProtectionStartPage + mPageCount;
4669
4670 mDirtyPages = std::vector<bool>(mPageCount);
4671 mDirtyPages.assign(mPageCount, true);
4672 }
4673
getDirtyPageRanges()4674 std::vector<PageRange> CoherentBuffer::getDirtyPageRanges()
4675 {
4676 std::vector<PageRange> dirtyPageRanges;
4677
4678 bool inDirty = false;
4679 for (size_t i = 0; i < mPageCount; i++)
4680 {
4681 if (!inDirty && mDirtyPages[i])
4682 {
4683 // Found start of a dirty range
4684 inDirty = true;
4685 // Set end page as last page initially
4686 dirtyPageRanges.push_back(PageRange(i, mPageCount));
4687 }
4688 else if (inDirty && !mDirtyPages[i])
4689 {
4690 // Found end of a dirty range
4691 inDirty = false;
4692 dirtyPageRanges.back().end = i;
4693 }
4694 }
4695
4696 return dirtyPageRanges;
4697 }
4698
getRange()4699 AddressRange CoherentBuffer::getRange()
4700 {
4701 return mRange;
4702 }
4703
getDirtyAddressRange(const PageRange & dirtyPageRange)4704 AddressRange CoherentBuffer::getDirtyAddressRange(const PageRange &dirtyPageRange)
4705 {
4706 AddressRange range;
4707
4708 if (dirtyPageRange.start == 0)
4709 {
4710 // First page, use non page aligned buffer start.
4711 range.start = mRange.start;
4712 }
4713 else
4714 {
4715 range.start = mProtectionRange.start + dirtyPageRange.start * mPageSize;
4716 }
4717
4718 if (dirtyPageRange.end == mPageCount)
4719 {
4720 // Last page, use non page aligned buffer end.
4721 range.size = mRange.end() - range.start;
4722 }
4723 else
4724 {
4725 range.size = (dirtyPageRange.end - dirtyPageRange.start) * mPageSize;
4726 // This occurs when a buffer occupies 2 pages, but is smaller than a page.
4727 if (mRange.end() < range.end())
4728 {
4729 range.size = mRange.end() - range.start;
4730 }
4731 }
4732
4733 // Dirty range must be in buffer
4734 ASSERT(range.start >= mRange.start && mRange.end() >= range.end());
4735
4736 return range;
4737 }
4738
~CoherentBuffer()4739 CoherentBuffer::~CoherentBuffer() {}
4740
isDirty()4741 bool CoherentBuffer::isDirty()
4742 {
4743 return std::find(mDirtyPages.begin(), mDirtyPages.end(), true) != mDirtyPages.end();
4744 }
4745
contains(size_t page,size_t * relativePage)4746 bool CoherentBuffer::contains(size_t page, size_t *relativePage)
4747 {
4748 bool isInProtectionRange = page >= mProtectionStartPage && page < mProtectionEndPage;
4749 if (!isInProtectionRange)
4750 {
4751 return false;
4752 }
4753
4754 *relativePage = page - mProtectionStartPage;
4755
4756 ASSERT(page >= mProtectionStartPage);
4757
4758 return true;
4759 }
4760
protectPageRange(const PageRange & pageRange)4761 void CoherentBuffer::protectPageRange(const PageRange &pageRange)
4762 {
4763 for (size_t i = pageRange.start; i < pageRange.end; i++)
4764 {
4765 setDirty(i, false);
4766 }
4767 }
4768
setDirty(size_t relativePage,bool dirty)4769 void CoherentBuffer::setDirty(size_t relativePage, bool dirty)
4770 {
4771 if (mDirtyPages[relativePage] == dirty)
4772 {
4773 // The page is already set.
4774 // This can happen when tracked buffers overlap in a page.
4775 return;
4776 }
4777
4778 uintptr_t pageStart = mProtectionRange.start + relativePage * mPageSize;
4779
4780 // Last page end must be the same as protection end
4781 if (relativePage + 1 == mPageCount)
4782 {
4783 ASSERT(mProtectionRange.end() == pageStart + mPageSize);
4784 }
4785
4786 bool ret;
4787 if (dirty)
4788 {
4789 ret = UnprotectMemory(pageStart, mPageSize);
4790 }
4791 else
4792 {
4793 ret = ProtectMemory(pageStart, mPageSize);
4794 }
4795
4796 if (!ret)
4797 {
4798 ERR() << "Could not set protection for buffer page " << relativePage << " at "
4799 << reinterpret_cast<void *>(pageStart) << " with size " << mPageSize;
4800 }
4801 mDirtyPages[relativePage] = dirty;
4802 }
4803
removeProtection(PageSharingType sharingType)4804 void CoherentBuffer::removeProtection(PageSharingType sharingType)
4805 {
4806 uintptr_t start = mProtectionRange.start;
4807 size_t size = mProtectionRange.size;
4808
4809 switch (sharingType)
4810 {
4811 case PageSharingType::FirstShared:
4812 case PageSharingType::FirstAndLastShared:
4813 start += mPageSize;
4814 break;
4815 default:
4816 break;
4817 }
4818
4819 switch (sharingType)
4820 {
4821 case PageSharingType::FirstShared:
4822 case PageSharingType::LastShared:
4823 size -= mPageSize;
4824 break;
4825 case PageSharingType::FirstAndLastShared:
4826 size -= (2 * mPageSize);
4827 break;
4828 default:
4829 break;
4830 }
4831
4832 if (size == 0)
4833 {
4834 return;
4835 }
4836
4837 if (!UnprotectMemory(start, size))
4838 {
4839 ERR() << "Could not remove protection for buffer at " << start << " with size " << size;
4840 }
4841 }
4842
CoherentBufferTracker()4843 CoherentBufferTracker::CoherentBufferTracker()
4844 {
4845 mPageSize = GetPageSize();
4846
4847 PageFaultCallback callback = [this](uintptr_t address) { return handleWrite(address); };
4848
4849 mPageFaultHandler = std::unique_ptr<PageFaultHandler>(CreatePageFaultHandler(callback));
4850 }
4851
~CoherentBufferTracker()4852 CoherentBufferTracker::~CoherentBufferTracker()
4853 {
4854 disable();
4855 }
4856
handleWrite(uintptr_t address)4857 PageFaultHandlerRangeType CoherentBufferTracker::handleWrite(uintptr_t address)
4858 {
4859 std::lock_guard<std::mutex> lock(mMutex);
4860 auto pagesInBuffers = getBufferPagesForAddress(address);
4861
4862 if (pagesInBuffers.empty())
4863 {
4864 ERR() << "Didn't find a tracked buffer containing " << reinterpret_cast<void *>(address);
4865 }
4866
4867 for (const auto &page : pagesInBuffers)
4868 {
4869 std::shared_ptr<CoherentBuffer> buffer = page.first;
4870 size_t relativePage = page.second;
4871 buffer->setDirty(relativePage, true);
4872 }
4873
4874 return pagesInBuffers.empty() ? PageFaultHandlerRangeType::OutOfRange
4875 : PageFaultHandlerRangeType::InRange;
4876 }
4877
getBufferPagesForAddress(uintptr_t address)4878 HashMap<std::shared_ptr<CoherentBuffer>, size_t> CoherentBufferTracker::getBufferPagesForAddress(
4879 uintptr_t address)
4880 {
4881 HashMap<std::shared_ptr<CoherentBuffer>, size_t> foundPages;
4882 size_t page = address / mPageSize;
4883 for (const auto &pair : mBuffers)
4884 {
4885 std::shared_ptr<CoherentBuffer> buffer = pair.second;
4886 size_t relativePage;
4887 if (buffer->contains(page, &relativePage))
4888 {
4889 foundPages.insert(std::make_pair(buffer, relativePage));
4890 }
4891 }
4892
4893 return foundPages;
4894 }
4895
isDirty(gl::BufferID id)4896 bool CoherentBufferTracker::isDirty(gl::BufferID id)
4897 {
4898 return mBuffers[id.value]->isDirty();
4899 }
4900
enable()4901 void CoherentBufferTracker::enable()
4902 {
4903 if (mEnabled)
4904 {
4905 return;
4906 }
4907
4908 bool ret = mPageFaultHandler->enable();
4909 if (ret)
4910 {
4911 mEnabled = true;
4912 }
4913 else
4914 {
4915 ERR() << "Could not enable page fault handler.";
4916 }
4917 }
4918
haveBuffer(gl::BufferID id)4919 bool CoherentBufferTracker::haveBuffer(gl::BufferID id)
4920 {
4921 return mBuffers.find(id.value) != mBuffers.end();
4922 }
4923
onEndFrame()4924 void CoherentBufferTracker::onEndFrame()
4925 {
4926 std::lock_guard<std::mutex> lock(mMutex);
4927
4928 if (!mEnabled)
4929 {
4930 return;
4931 }
4932
4933 // Remove protection from all buffers
4934 for (const auto &pair : mBuffers)
4935 {
4936 std::shared_ptr<CoherentBuffer> buffer = pair.second;
4937 buffer->removeProtection(PageSharingType::NoneShared);
4938 }
4939
4940 disable();
4941 }
4942
disable()4943 void CoherentBufferTracker::disable()
4944 {
4945 if (!mEnabled)
4946 {
4947 return;
4948 }
4949
4950 if (mPageFaultHandler->disable())
4951 {
4952 mEnabled = false;
4953 }
4954 else
4955 {
4956 ERR() << "Could not disable page fault handler.";
4957 }
4958 }
4959
addBuffer(gl::BufferID id,uintptr_t start,size_t size)4960 void CoherentBufferTracker::addBuffer(gl::BufferID id, uintptr_t start, size_t size)
4961 {
4962 std::lock_guard<std::mutex> lock(mMutex);
4963
4964 if (haveBuffer(id))
4965 {
4966 return;
4967 }
4968
4969 auto buffer = std::make_shared<CoherentBuffer>(start, size, mPageSize);
4970
4971 mBuffers.insert(std::make_pair(id.value, std::move(buffer)));
4972 }
4973
doesBufferSharePage(gl::BufferID id)4974 PageSharingType CoherentBufferTracker::doesBufferSharePage(gl::BufferID id)
4975 {
4976 bool firstPageShared = false;
4977 bool lastPageShared = false;
4978
4979 std::shared_ptr<CoherentBuffer> buffer = mBuffers[id.value];
4980
4981 AddressRange range = buffer->getRange();
4982
4983 size_t firstPage = range.start / mPageSize;
4984 size_t lastPage = range.end() / mPageSize;
4985
4986 for (const auto &pair : mBuffers)
4987 {
4988 gl::BufferID otherId = {pair.first};
4989 if (otherId != id)
4990 {
4991 std::shared_ptr<CoherentBuffer> otherBuffer = pair.second;
4992 size_t relativePage;
4993 if (otherBuffer->contains(firstPage, &relativePage))
4994 {
4995 firstPageShared = true;
4996 }
4997 else if (otherBuffer->contains(lastPage, &relativePage))
4998 {
4999 lastPageShared = true;
5000 }
5001 }
5002 }
5003
5004 if (firstPageShared && !lastPageShared)
5005 {
5006 return PageSharingType::FirstShared;
5007 }
5008 else if (!firstPageShared && lastPageShared)
5009 {
5010 return PageSharingType::LastShared;
5011 }
5012 else if (firstPageShared && lastPageShared)
5013 {
5014 return PageSharingType::FirstAndLastShared;
5015 }
5016 else
5017 {
5018 return PageSharingType::NoneShared;
5019 }
5020 }
5021
removeBuffer(gl::BufferID id)5022 void CoherentBufferTracker::removeBuffer(gl::BufferID id)
5023 {
5024 std::lock_guard<std::mutex> lock(mMutex);
5025
5026 if (!haveBuffer(id))
5027 {
5028 return;
5029 }
5030
5031 // If the buffer shares pages with other tracked buffers,
5032 // don't unprotect the overlapping pages.
5033 PageSharingType sharingType = doesBufferSharePage(id);
5034 mBuffers[id.value]->removeProtection(sharingType);
5035 mBuffers.erase(id.value);
5036 }
5037
trackBufferMapping(CallCapture * call,gl::BufferID id,gl::Buffer * buffer,GLintptr offset,GLsizeiptr length,bool writable,bool coherent)5038 void FrameCaptureShared::trackBufferMapping(CallCapture *call,
5039 gl::BufferID id,
5040 gl::Buffer *buffer,
5041 GLintptr offset,
5042 GLsizeiptr length,
5043 bool writable,
5044 bool coherent)
5045 {
5046 // Track that the buffer was mapped
5047 mResourceTracker.setBufferMapped(id.value);
5048
5049 if (writable)
5050 {
5051 // If this buffer was mapped writable, we don't have any visibility into what
5052 // happens to it. Therefore, remember the details about it, and we'll read it back
5053 // on Unmap to repopulate it during replay.
5054 mBufferDataMap[id] = std::make_pair(offset, length);
5055
5056 // Track that this buffer was potentially modified
5057 mResourceTracker.getTrackedResource(ResourceIDType::Buffer).setModifiedResource(id.value);
5058
5059 // Track the bufferID that was just mapped for use when writing return value
5060 call->params.setMappedBufferID(id);
5061
5062 // Track coherent buffer
5063 // Check if capture is active to not initialize the coherent buffer tracker on the
5064 // first coherent glMapBufferRange call.
5065 if (coherent && (isCaptureActive() || mMidExecutionCaptureActive))
5066 {
5067 mCoherentBufferTracker.enable();
5068 uintptr_t data = reinterpret_cast<uintptr_t>(buffer->getMapPointer());
5069 mCoherentBufferTracker.addBuffer(id, data, length);
5070 }
5071 }
5072 }
5073
trackTextureUpdate(const gl::Context * context,const CallCapture & call)5074 void FrameCaptureShared::trackTextureUpdate(const gl::Context *context, const CallCapture &call)
5075 {
5076 int index = 0;
5077 std::string paramName = "targetPacked";
5078 ParamType paramType = ParamType::TTextureTarget;
5079
5080 // Some calls provide the textureID directly
5081 // For the rest, look it up based on the currently bound texture
5082 switch (call.entryPoint)
5083 {
5084 case EntryPoint::GLCompressedCopyTextureCHROMIUM:
5085 index = 1;
5086 paramName = "destIdPacked";
5087 paramType = ParamType::TTextureID;
5088 break;
5089 case EntryPoint::GLCopyTextureCHROMIUM:
5090 case EntryPoint::GLCopySubTextureCHROMIUM:
5091 case EntryPoint::GLCopyTexture3DANGLE:
5092 index = 3;
5093 paramName = "destIdPacked";
5094 paramType = ParamType::TTextureID;
5095 break;
5096 case EntryPoint::GLCopyImageSubData:
5097 case EntryPoint::GLCopyImageSubDataEXT:
5098 case EntryPoint::GLCopyImageSubDataOES:
5099 index = 7;
5100 paramName = "dstTarget";
5101 paramType = ParamType::TGLenum;
5102 break;
5103 default:
5104 break;
5105 }
5106
5107 GLuint id = 0;
5108 switch (paramType)
5109 {
5110 case ParamType::TTextureTarget:
5111 {
5112 gl::TextureTarget targetPacked =
5113 call.params.getParam(paramName.c_str(), ParamType::TTextureTarget, index)
5114 .value.TextureTargetVal;
5115 gl::TextureType textureType = gl::TextureTargetToType(targetPacked);
5116 gl::Texture *texture = context->getState().getTargetTexture(textureType);
5117 id = texture->id().value;
5118 break;
5119 }
5120 case ParamType::TTextureID:
5121 {
5122 gl::TextureID destIDPacked =
5123 call.params.getParam(paramName.c_str(), ParamType::TTextureID, index)
5124 .value.TextureIDVal;
5125 id = destIDPacked.value;
5126 break;
5127 }
5128 case ParamType::TGLenum:
5129 {
5130 GLenum target =
5131 call.params.getParam(paramName.c_str(), ParamType::TGLenum, index).value.GLenumVal;
5132 gl::TextureTarget targetPacked = gl::PackParam<gl::TextureTarget>(target);
5133 gl::TextureType textureType = gl::TextureTargetToType(targetPacked);
5134 gl::Texture *texture = context->getState().getTargetTexture(textureType);
5135 id = texture->id().value;
5136 break;
5137 }
5138 default:
5139 ERR() << "Unhandled paramType= " << static_cast<int>(paramType);
5140 UNREACHABLE();
5141 break;
5142 }
5143
5144 // Mark it as modified
5145 mResourceTracker.getTrackedResource(ResourceIDType::Texture).setModifiedResource(id);
5146 }
5147
updateCopyImageSubData(CallCapture & call)5148 void FrameCaptureShared::updateCopyImageSubData(CallCapture &call)
5149 {
5150 // This call modifies srcName and dstName to no longer be object IDs (GLuint), but actual
5151 // packed types that can remapped using gTextureMap and gRenderbufferMap
5152
5153 GLint srcName = call.params.getParam("srcName", ParamType::TGLuint, 0).value.GLuintVal;
5154 GLenum srcTarget = call.params.getParam("srcTarget", ParamType::TGLenum, 1).value.GLenumVal;
5155 switch (srcTarget)
5156 {
5157 case GL_RENDERBUFFER:
5158 {
5159 // Convert the GLuint to RenderbufferID
5160 gl::RenderbufferID srcRenderbufferID = {static_cast<GLuint>(srcName)};
5161 call.params.setValueParamAtIndex("srcName", ParamType::TRenderbufferID,
5162 srcRenderbufferID, 0);
5163 break;
5164 }
5165 case GL_TEXTURE_2D:
5166 case GL_TEXTURE_2D_ARRAY:
5167 case GL_TEXTURE_3D:
5168 case GL_TEXTURE_CUBE_MAP:
5169 {
5170 // Convert the GLuint to TextureID
5171 gl::TextureID srcTextureID = {static_cast<GLuint>(srcName)};
5172 call.params.setValueParamAtIndex("srcName", ParamType::TTextureID, srcTextureID, 0);
5173 break;
5174 }
5175 default:
5176 ERR() << "Unhandled srcTarget = " << srcTarget;
5177 UNREACHABLE();
5178 break;
5179 }
5180
5181 // Change dstName to the appropriate type based on dstTarget
5182 GLint dstName = call.params.getParam("dstName", ParamType::TGLuint, 6).value.GLuintVal;
5183 GLenum dstTarget = call.params.getParam("dstTarget", ParamType::TGLenum, 7).value.GLenumVal;
5184 switch (dstTarget)
5185 {
5186 case GL_RENDERBUFFER:
5187 {
5188 // Convert the GLuint to RenderbufferID
5189 gl::RenderbufferID dstRenderbufferID = {static_cast<GLuint>(dstName)};
5190 call.params.setValueParamAtIndex("dstName", ParamType::TRenderbufferID,
5191 dstRenderbufferID, 6);
5192 break;
5193 }
5194 case GL_TEXTURE_2D:
5195 case GL_TEXTURE_2D_ARRAY:
5196 case GL_TEXTURE_3D:
5197 case GL_TEXTURE_CUBE_MAP:
5198 {
5199 // Convert the GLuint to TextureID
5200 gl::TextureID dstTextureID = {static_cast<GLuint>(dstName)};
5201 call.params.setValueParamAtIndex("dstName", ParamType::TTextureID, dstTextureID, 6);
5202 break;
5203 }
5204 default:
5205 ERR() << "Unhandled dstTarget = " << dstTarget;
5206 UNREACHABLE();
5207 break;
5208 }
5209 }
5210
overrideProgramBinary(const gl::Context * context,CallCapture & inCall,std::vector<CallCapture> & outCalls)5211 void FrameCaptureShared::overrideProgramBinary(const gl::Context *context,
5212 CallCapture &inCall,
5213 std::vector<CallCapture> &outCalls)
5214 {
5215 // Program binaries are inherently non-portable, even between two ANGLE builds.
5216 // If an application is using glProgramBinary in the middle of a trace, we need to replace
5217 // those calls with an equivalent sequence of portable calls.
5218 //
5219 // For example, here is a sequence an app could use for glProgramBinary:
5220 //
5221 // gShaderProgramMap[42] = glCreateProgram();
5222 // glProgramBinary(gShaderProgramMap[42], GL_PROGRAM_BINARY_ANGLE, gBinaryData[x], 1000);
5223 // glGetProgramiv(gShaderProgramMap[42], GL_LINK_STATUS, gReadBuffer);
5224 // glGetProgramiv(gShaderProgramMap[42], GL_PROGRAM_BINARY_LENGTH, gReadBuffer);
5225 //
5226 // With this override, the glProgramBinary call will be replaced like so:
5227 //
5228 // gShaderProgramMap[42] = glCreateProgram();
5229 // === Begin override ===
5230 // gShaderProgramMap[43] = glCreateShader(GL_VERTEX_SHADER);
5231 // glShaderSource(gShaderProgramMap[43], 1, string_0, &gBinaryData[100]);
5232 // glCompileShader(gShaderProgramMap[43]);
5233 // glAttachShader(gShaderProgramMap[42], gShaderProgramMap[43]);
5234 // glDeleteShader(gShaderProgramMap[43]);
5235 // gShaderProgramMap[43] = glCreateShader(GL_FRAGMENT_SHADER);
5236 // glShaderSource(gShaderProgramMap[43], 1, string_1, &gBinaryData[200]);
5237 // glCompileShader(gShaderProgramMap[43]);
5238 // glAttachShader(gShaderProgramMap[42], gShaderProgramMap[43]);
5239 // glDeleteShader(gShaderProgramMap[43]);
5240 // glBindAttribLocation(gShaderProgramMap[42], 0, "attrib1");
5241 // glBindAttribLocation(gShaderProgramMap[42], 1, "attrib2");
5242 // glLinkProgram(gShaderProgramMap[42]);
5243 // UpdateUniformLocation(gShaderProgramMap[42], "foo", 0, 20);
5244 // UpdateUniformLocation(gShaderProgramMap[42], "bar", 72, 1);
5245 // glUseProgram(gShaderProgramMap[42]);
5246 // UpdateCurrentProgram(gShaderProgramMap[42]);
5247 // glUniform4fv(gUniformLocations[gCurrentProgram][0], 20, &gBinaryData[300]);
5248 // glUniform1iv(gUniformLocations[gCurrentProgram][72], 1, &gBinaryData[400]);
5249 // === End override ===
5250 // glGetProgramiv(gShaderProgramMap[42], GL_LINK_STATUS, gReadBuffer);
5251 // glGetProgramiv(gShaderProgramMap[42], GL_PROGRAM_BINARY_LENGTH, gReadBuffer);
5252 //
5253 // To facilitate this override, we are serializing each shader stage source into the binary
5254 // itself. See Program::serialize and Program::deserialize. Once extracted from the binary,
5255 // they will be available via getProgramSources.
5256
5257 gl::ShaderProgramID id = inCall.params.getParam("programPacked", ParamType::TShaderProgramID, 0)
5258 .value.ShaderProgramIDVal;
5259
5260 gl::Program *program = context->getProgramResolveLink(id);
5261 ASSERT(program);
5262
5263 mResourceTracker.onShaderProgramAccess(id);
5264 gl::ShaderProgramID tempShaderStartID = {mResourceTracker.getMaxShaderPrograms()};
5265 GenerateLinkedProgram(context, context->getState(), &mResourceTracker, &outCalls, program, id,
5266 tempShaderStartID, getProgramSources(id));
5267 }
5268
maybeOverrideEntryPoint(const gl::Context * context,CallCapture & inCall,std::vector<CallCapture> & outCalls)5269 void FrameCaptureShared::maybeOverrideEntryPoint(const gl::Context *context,
5270 CallCapture &inCall,
5271 std::vector<CallCapture> &outCalls)
5272 {
5273 switch (inCall.entryPoint)
5274 {
5275 case EntryPoint::GLEGLImageTargetTexture2DOES:
5276 {
5277 // We don't support reading EGLImages. Instead, just pull from a tiny null texture.
5278 // TODO (anglebug.com/4964): Read back the image data and populate the texture.
5279 std::vector<uint8_t> pixelData = {0, 0, 0, 0};
5280 outCalls.emplace_back(
5281 CaptureTexSubImage2D(context->getState(), true, gl::TextureTarget::_2D, 0, 0, 0, 1,
5282 1, GL_RGBA, GL_UNSIGNED_BYTE, pixelData.data()));
5283 break;
5284 }
5285 case EntryPoint::GLEGLImageTargetRenderbufferStorageOES:
5286 {
5287 UNIMPLEMENTED();
5288 break;
5289 }
5290 case EntryPoint::GLCopyImageSubData:
5291 case EntryPoint::GLCopyImageSubDataEXT:
5292 case EntryPoint::GLCopyImageSubDataOES:
5293 {
5294 // We must look at the src and dst target types to determine which remap table to use
5295 updateCopyImageSubData(inCall);
5296 outCalls.emplace_back(std::move(inCall));
5297 break;
5298 }
5299 case EntryPoint::GLProgramBinary:
5300 case EntryPoint::GLProgramBinaryOES:
5301 {
5302 // Binary formats are not portable at all, so replace the calls with full linking
5303 // sequence
5304 overrideProgramBinary(context, inCall, outCalls);
5305 break;
5306 }
5307 default:
5308 {
5309 // Pass the single call through
5310 outCalls.emplace_back(std::move(inCall));
5311 break;
5312 }
5313 }
5314 }
5315
maybeCaptureCoherentBuffers(const gl::Context * context)5316 void FrameCaptureShared::maybeCaptureCoherentBuffers(const gl::Context *context)
5317 {
5318 if (!isCaptureActive())
5319 {
5320 return;
5321 }
5322
5323 std::lock_guard<std::mutex> lock(mCoherentBufferTracker.mMutex);
5324
5325 for (const auto &pair : mCoherentBufferTracker.mBuffers)
5326 {
5327 gl::BufferID id = {pair.first};
5328 if (mCoherentBufferTracker.isDirty(id))
5329 {
5330 captureCoherentBufferSnapshot(context, id);
5331 }
5332 }
5333 }
5334
maybeCaptureDrawArraysClientData(const gl::Context * context,CallCapture & call,size_t instanceCount)5335 void FrameCaptureShared::maybeCaptureDrawArraysClientData(const gl::Context *context,
5336 CallCapture &call,
5337 size_t instanceCount)
5338 {
5339 if (!context->getStateCache().hasAnyActiveClientAttrib())
5340 {
5341 return;
5342 }
5343
5344 // Get counts from paramBuffer.
5345 GLint firstVertex =
5346 call.params.getParamFlexName("first", "start", ParamType::TGLint, 1).value.GLintVal;
5347 GLsizei drawCount = call.params.getParam("count", ParamType::TGLsizei, 2).value.GLsizeiVal;
5348 captureClientArraySnapshot(context, firstVertex + drawCount, instanceCount);
5349 }
5350
maybeCaptureDrawElementsClientData(const gl::Context * context,CallCapture & call,size_t instanceCount)5351 void FrameCaptureShared::maybeCaptureDrawElementsClientData(const gl::Context *context,
5352 CallCapture &call,
5353 size_t instanceCount)
5354 {
5355 if (!context->getStateCache().hasAnyActiveClientAttrib())
5356 {
5357 return;
5358 }
5359
5360 // if the count is zero then the index evaluation is not valid and we wouldn't be drawing
5361 // anything anyway, so skip capturing
5362 GLsizei count = call.params.getParam("count", ParamType::TGLsizei, 1).value.GLsizeiVal;
5363 if (count == 0)
5364 {
5365 return;
5366 }
5367
5368 gl::DrawElementsType drawElementsType =
5369 call.params.getParam("typePacked", ParamType::TDrawElementsType, 2)
5370 .value.DrawElementsTypeVal;
5371 const void *indices =
5372 call.params.getParam("indices", ParamType::TvoidConstPointer, 3).value.voidConstPointerVal;
5373
5374 gl::IndexRange indexRange;
5375
5376 bool restart = context->getState().isPrimitiveRestartEnabled();
5377
5378 gl::Buffer *elementArrayBuffer = context->getState().getVertexArray()->getElementArrayBuffer();
5379 if (elementArrayBuffer)
5380 {
5381 size_t offset = reinterpret_cast<size_t>(indices);
5382 (void)elementArrayBuffer->getIndexRange(context, drawElementsType, offset, count, restart,
5383 &indexRange);
5384 }
5385 else
5386 {
5387 ASSERT(indices);
5388 indexRange = gl::ComputeIndexRange(drawElementsType, indices, count, restart);
5389 }
5390
5391 // index starts from 0
5392 captureClientArraySnapshot(context, indexRange.end + 1, instanceCount);
5393 }
5394
maybeCapturePreCallUpdates(const gl::Context * context,CallCapture & call,std::vector<CallCapture> * shareGroupSetupCalls,ResourceIDToSetupCallsMap * resourceIDToSetupCalls)5395 void FrameCaptureShared::maybeCapturePreCallUpdates(
5396 const gl::Context *context,
5397 CallCapture &call,
5398 std::vector<CallCapture> *shareGroupSetupCalls,
5399 ResourceIDToSetupCallsMap *resourceIDToSetupCalls)
5400 {
5401 switch (call.entryPoint)
5402 {
5403 case EntryPoint::GLVertexAttribPointer:
5404 case EntryPoint::GLVertexPointer:
5405 case EntryPoint::GLColorPointer:
5406 case EntryPoint::GLTexCoordPointer:
5407 case EntryPoint::GLNormalPointer:
5408 case EntryPoint::GLPointSizePointerOES:
5409 {
5410 // Get array location
5411 GLuint index = 0;
5412 if (call.entryPoint == EntryPoint::GLVertexAttribPointer)
5413 {
5414 index = call.params.getParam("index", ParamType::TGLuint, 0).value.GLuintVal;
5415 }
5416 else
5417 {
5418 gl::ClientVertexArrayType type;
5419 switch (call.entryPoint)
5420 {
5421 case EntryPoint::GLVertexPointer:
5422 type = gl::ClientVertexArrayType::Vertex;
5423 break;
5424 case EntryPoint::GLColorPointer:
5425 type = gl::ClientVertexArrayType::Color;
5426 break;
5427 case EntryPoint::GLTexCoordPointer:
5428 type = gl::ClientVertexArrayType::TextureCoord;
5429 break;
5430 case EntryPoint::GLNormalPointer:
5431 type = gl::ClientVertexArrayType::Normal;
5432 break;
5433 case EntryPoint::GLPointSizePointerOES:
5434 type = gl::ClientVertexArrayType::PointSize;
5435 break;
5436 default:
5437 UNREACHABLE();
5438 type = gl::ClientVertexArrayType::InvalidEnum;
5439 }
5440 index = gl::GLES1Renderer::VertexArrayIndex(type, context->getState().gles1());
5441 }
5442
5443 if (call.params.hasClientArrayData())
5444 {
5445 mClientVertexArrayMap[index] = static_cast<int>(mFrameCalls.size());
5446 }
5447 else
5448 {
5449 mClientVertexArrayMap[index] = -1;
5450 }
5451 break;
5452 }
5453
5454 case EntryPoint::GLGenFramebuffers:
5455 case EntryPoint::GLGenFramebuffersOES:
5456 {
5457 GLsizei count = call.params.getParam("n", ParamType::TGLsizei, 0).value.GLsizeiVal;
5458 const gl::FramebufferID *framebufferIDs =
5459 call.params.getParam("framebuffersPacked", ParamType::TFramebufferIDPointer, 1)
5460 .value.FramebufferIDPointerVal;
5461 for (GLsizei i = 0; i < count; i++)
5462 {
5463 handleGennedResource(framebufferIDs[i]);
5464 }
5465 break;
5466 }
5467
5468 case EntryPoint::GLBindFramebuffer:
5469 case EntryPoint::GLBindFramebufferOES:
5470 maybeGenResourceOnBind<gl::FramebufferID>(call);
5471 break;
5472
5473 case EntryPoint::GLGenRenderbuffers:
5474 case EntryPoint::GLGenRenderbuffersOES:
5475 {
5476 GLsizei count = call.params.getParam("n", ParamType::TGLsizei, 0).value.GLsizeiVal;
5477 const gl::RenderbufferID *renderbufferIDs =
5478 call.params.getParam("renderbuffersPacked", ParamType::TRenderbufferIDPointer, 1)
5479 .value.RenderbufferIDPointerVal;
5480 for (GLsizei i = 0; i < count; i++)
5481 {
5482 handleGennedResource(renderbufferIDs[i]);
5483 }
5484 break;
5485 }
5486
5487 case EntryPoint::GLBindRenderbuffer:
5488 case EntryPoint::GLBindRenderbufferOES:
5489 maybeGenResourceOnBind<gl::RenderbufferID>(call);
5490 break;
5491
5492 case EntryPoint::GLGenTextures:
5493 {
5494 GLsizei count = call.params.getParam("n", ParamType::TGLsizei, 0).value.GLsizeiVal;
5495 const gl::TextureID *textureIDs =
5496 call.params.getParam("texturesPacked", ParamType::TTextureIDPointer, 1)
5497 .value.TextureIDPointerVal;
5498 for (GLsizei i = 0; i < count; i++)
5499 {
5500 // If we're capturing, track what new textures have been genned
5501 handleGennedResource(textureIDs[i]);
5502 }
5503 break;
5504 }
5505
5506 case EntryPoint::GLBindTexture:
5507 maybeGenResourceOnBind<gl::TextureID>(call);
5508 break;
5509
5510 case EntryPoint::GLDeleteBuffers:
5511 {
5512 GLsizei count = call.params.getParam("n", ParamType::TGLsizei, 0).value.GLsizeiVal;
5513 const gl::BufferID *bufferIDs =
5514 call.params.getParam("buffersPacked", ParamType::TBufferIDConstPointer, 1)
5515 .value.BufferIDConstPointerVal;
5516 for (GLsizei i = 0; i < count; i++)
5517 {
5518 // For each buffer being deleted, check our backup of data and remove it
5519 const auto &bufferDataInfo = mBufferDataMap.find(bufferIDs[i]);
5520 if (bufferDataInfo != mBufferDataMap.end())
5521 {
5522 mBufferDataMap.erase(bufferDataInfo);
5523 }
5524 // If we're capturing, track what buffers have been deleted
5525 handleDeletedResource(bufferIDs[i]);
5526 }
5527 break;
5528 }
5529
5530 case EntryPoint::GLGenBuffers:
5531 {
5532 GLsizei count = call.params.getParam("n", ParamType::TGLsizei, 0).value.GLsizeiVal;
5533 const gl::BufferID *bufferIDs =
5534 call.params.getParam("buffersPacked", ParamType::TBufferIDPointer, 1)
5535 .value.BufferIDPointerVal;
5536 for (GLsizei i = 0; i < count; i++)
5537 {
5538 handleGennedResource(bufferIDs[i]);
5539 }
5540 break;
5541 }
5542
5543 case EntryPoint::GLBindBuffer:
5544 maybeGenResourceOnBind<gl::BufferID>(call);
5545 break;
5546
5547 case EntryPoint::GLDeleteProgramPipelines:
5548 case EntryPoint::GLDeleteProgramPipelinesEXT:
5549 {
5550 GLsizei count = call.params.getParam("n", ParamType::TGLsizei, 0).value.GLsizeiVal;
5551 const gl::ProgramPipelineID *pipelineIDs =
5552 call.params
5553 .getParam("pipelinesPacked", ParamType::TProgramPipelineIDConstPointer, 1)
5554 .value.ProgramPipelineIDPointerVal;
5555 for (GLsizei i = 0; i < count; i++)
5556 {
5557 handleDeletedResource(pipelineIDs[i]);
5558 }
5559 break;
5560 }
5561
5562 case EntryPoint::GLGenProgramPipelines:
5563 case EntryPoint::GLGenProgramPipelinesEXT:
5564 {
5565 GLsizei count = call.params.getParam("n", ParamType::TGLsizei, 0).value.GLsizeiVal;
5566 const gl::ProgramPipelineID *pipelineIDs =
5567 call.params.getParam("pipelinesPacked", ParamType::TProgramPipelineIDPointer, 1)
5568 .value.ProgramPipelineIDPointerVal;
5569 for (GLsizei i = 0; i < count; i++)
5570 {
5571 handleGennedResource(pipelineIDs[i]);
5572 }
5573 break;
5574 }
5575
5576 case EntryPoint::GLDeleteSync:
5577 {
5578 GLsync sync = call.params.getParam("sync", ParamType::TGLsync, 0).value.GLsyncVal;
5579 FrameCaptureShared *frameCaptureShared =
5580 context->getShareGroup()->getFrameCaptureShared();
5581 // If we're capturing, track which fence sync has been deleted
5582 if (frameCaptureShared->isCaptureActive())
5583 {
5584 mResourceTracker.setDeletedFenceSync(sync);
5585 }
5586 break;
5587 }
5588
5589 case EntryPoint::GLDrawArrays:
5590 {
5591 maybeCaptureDrawArraysClientData(context, call, 1);
5592 maybeCaptureCoherentBuffers(context);
5593 break;
5594 }
5595
5596 case EntryPoint::GLDrawArraysInstanced:
5597 case EntryPoint::GLDrawArraysInstancedANGLE:
5598 case EntryPoint::GLDrawArraysInstancedEXT:
5599 {
5600 GLsizei instancecount =
5601 call.params.getParamFlexName("instancecount", "primcount", ParamType::TGLsizei, 3)
5602 .value.GLsizeiVal;
5603
5604 maybeCaptureDrawArraysClientData(context, call, instancecount);
5605 maybeCaptureCoherentBuffers(context);
5606 break;
5607 }
5608
5609 case EntryPoint::GLDrawElements:
5610 {
5611 maybeCaptureDrawElementsClientData(context, call, 1);
5612 maybeCaptureCoherentBuffers(context);
5613 break;
5614 }
5615
5616 case EntryPoint::GLDrawElementsInstanced:
5617 case EntryPoint::GLDrawElementsInstancedANGLE:
5618 case EntryPoint::GLDrawElementsInstancedEXT:
5619 {
5620 GLsizei instancecount =
5621 call.params.getParamFlexName("instancecount", "primcount", ParamType::TGLsizei, 4)
5622 .value.GLsizeiVal;
5623
5624 maybeCaptureDrawElementsClientData(context, call, instancecount);
5625 maybeCaptureCoherentBuffers(context);
5626 break;
5627 }
5628
5629 case EntryPoint::GLCreateShaderProgramv:
5630 {
5631 // Refresh the cached shader sources.
5632 // The command CreateShaderProgramv() creates a stand-alone program from an array of
5633 // null-terminated source code strings for a single shader type, so we need update the
5634 // Shader and Program sources, similar to GLCompileShader + GLLinkProgram handling.
5635 gl::ShaderProgramID programID = {call.params.getReturnValue().value.GLuintVal};
5636 const ParamCapture ¶mCapture =
5637 call.params.getParam("typePacked", ParamType::TShaderType, 0);
5638 const ParamCapture &lineCount = call.params.getParam("count", ParamType::TGLsizei, 1);
5639 const ParamCapture &strings =
5640 call.params.getParam("strings", ParamType::TGLcharConstPointerPointer, 2);
5641
5642 std::ostringstream sourceString;
5643 for (int i = 0; i < lineCount.value.GLsizeiVal; ++i)
5644 {
5645 sourceString << strings.value.GLcharConstPointerPointerVal[i];
5646 }
5647
5648 gl::ShaderType shaderType = paramCapture.value.ShaderTypeVal;
5649 ProgramSources source;
5650 source[shaderType] = sourceString.str();
5651 setProgramSources(programID, source);
5652 handleGennedResource(programID);
5653 break;
5654 }
5655
5656 case EntryPoint::GLCreateProgram:
5657 {
5658 // If we're capturing, track which programs have been created
5659 gl::ShaderProgramID programID = {call.params.getReturnValue().value.GLuintVal};
5660 handleGennedResource(programID);
5661 break;
5662 }
5663
5664 case EntryPoint::GLDeleteProgram:
5665 {
5666 // If we're capturing, track which programs have been deleted
5667 const ParamCapture ¶m =
5668 call.params.getParam("programPacked", ParamType::TShaderProgramID, 0);
5669 handleDeletedResource(param.value.ShaderProgramIDVal);
5670 break;
5671 }
5672
5673 case EntryPoint::GLCompileShader:
5674 {
5675 // Refresh the cached shader sources.
5676 gl::ShaderProgramID shaderID =
5677 call.params.getParam("shaderPacked", ParamType::TShaderProgramID, 0)
5678 .value.ShaderProgramIDVal;
5679 const gl::Shader *shader = context->getShader(shaderID);
5680 // Shaders compiled for ProgramBinary will not have a shader created
5681 if (shader)
5682 {
5683 setShaderSource(shaderID, shader->getSourceString());
5684 }
5685 break;
5686 }
5687
5688 case EntryPoint::GLLinkProgram:
5689 {
5690 // Refresh the cached program sources.
5691 gl::ShaderProgramID programID =
5692 call.params.getParam("programPacked", ParamType::TShaderProgramID, 0)
5693 .value.ShaderProgramIDVal;
5694 const gl::Program *program = context->getProgramResolveLink(programID);
5695 // Programs linked in support of ProgramBinary will not have attached shaders
5696 if (program->getState().hasAttachedShader())
5697 {
5698 setProgramSources(programID, GetAttachedProgramSources(program));
5699 }
5700 break;
5701 }
5702
5703 case EntryPoint::GLCompressedTexImage1D:
5704 case EntryPoint::GLCompressedTexSubImage1D:
5705 {
5706 UNIMPLEMENTED();
5707 break;
5708 }
5709
5710 case EntryPoint::GLDeleteTextures:
5711 {
5712 // Free any TextureLevelDataMap entries being tracked for this texture
5713 // This is to cover the scenario where a texture has been created, its
5714 // levels cached, then texture deleted and recreated, receiving the same ID
5715
5716 // Look up how many textures are being deleted
5717 GLsizei n = call.params.getParam("n", ParamType::TGLsizei, 0).value.GLsizeiVal;
5718
5719 // Look up the pointer to list of textures
5720 const gl::TextureID *textureIDs =
5721 call.params.getParam("texturesPacked", ParamType::TTextureIDConstPointer, 1)
5722 .value.TextureIDConstPointerVal;
5723
5724 // For each texture listed for deletion
5725 for (int32_t i = 0; i < n; ++i)
5726 {
5727 // If we're capturing, track what textures have been deleted
5728 handleDeletedResource(textureIDs[i]);
5729 }
5730 break;
5731 }
5732
5733 case EntryPoint::GLMapBuffer:
5734 case EntryPoint::GLMapBufferOES:
5735 {
5736 gl::BufferBinding target =
5737 call.params.getParam("targetPacked", ParamType::TBufferBinding, 0)
5738 .value.BufferBindingVal;
5739
5740 GLbitfield access =
5741 call.params.getParam("access", ParamType::TGLenum, 1).value.GLenumVal;
5742
5743 gl::Buffer *buffer = context->getState().getTargetBuffer(target);
5744
5745 GLintptr offset = 0;
5746 GLsizeiptr length = static_cast<GLsizeiptr>(buffer->getSize());
5747
5748 bool writable =
5749 access == GL_WRITE_ONLY_OES || access == GL_WRITE_ONLY || access == GL_READ_WRITE;
5750
5751 FrameCaptureShared *frameCaptureShared =
5752 context->getShareGroup()->getFrameCaptureShared();
5753 frameCaptureShared->trackBufferMapping(&call, buffer->id(), buffer, offset, length,
5754 writable, false);
5755 break;
5756 }
5757
5758 case EntryPoint::GLUnmapNamedBuffer:
5759 {
5760 UNIMPLEMENTED();
5761 break;
5762 }
5763
5764 case EntryPoint::GLMapBufferRange:
5765 case EntryPoint::GLMapBufferRangeEXT:
5766 {
5767 GLintptr offset =
5768 call.params.getParam("offset", ParamType::TGLintptr, 1).value.GLintptrVal;
5769 GLsizeiptr length =
5770 call.params.getParam("length", ParamType::TGLsizeiptr, 2).value.GLsizeiptrVal;
5771 GLbitfield access =
5772 call.params.getParam("access", ParamType::TGLbitfield, 3).value.GLbitfieldVal;
5773
5774 gl::BufferBinding target =
5775 call.params.getParam("targetPacked", ParamType::TBufferBinding, 0)
5776 .value.BufferBindingVal;
5777 gl::Buffer *buffer = context->getState().getTargetBuffer(target);
5778
5779 FrameCaptureShared *frameCaptureShared =
5780 context->getShareGroup()->getFrameCaptureShared();
5781 frameCaptureShared->trackBufferMapping(&call, buffer->id(), buffer, offset, length,
5782 access & GL_MAP_WRITE_BIT,
5783 access & GL_MAP_COHERENT_BIT_EXT);
5784 break;
5785 }
5786
5787 case EntryPoint::GLUnmapBuffer:
5788 case EntryPoint::GLUnmapBufferOES:
5789 {
5790 // See if we need to capture the buffer contents
5791 captureMappedBufferSnapshot(context, call);
5792
5793 // Track that the buffer was unmapped, for use during state reset
5794 gl::BufferBinding target =
5795 call.params.getParam("targetPacked", ParamType::TBufferBinding, 0)
5796 .value.BufferBindingVal;
5797 gl::Buffer *buffer = context->getState().getTargetBuffer(target);
5798 mResourceTracker.setBufferUnmapped(buffer->id().value);
5799
5800 // Remove from CoherentBufferTracker
5801 mCoherentBufferTracker.removeBuffer(buffer->id());
5802 break;
5803 }
5804
5805 case EntryPoint::GLBufferData:
5806 case EntryPoint::GLBufferSubData:
5807 {
5808 gl::BufferBinding target =
5809 call.params.getParam("targetPacked", ParamType::TBufferBinding, 0)
5810 .value.BufferBindingVal;
5811
5812 gl::Buffer *buffer = context->getState().getTargetBuffer(target);
5813
5814 // Track that this buffer's contents have been modified
5815 mResourceTracker.getTrackedResource(ResourceIDType::Buffer)
5816 .setModifiedResource(buffer->id().value);
5817
5818 // BufferData is equivalent to UnmapBuffer, for what we're tracking.
5819 // From the ES 3.1 spec in BufferData section:
5820 // If any portion of the buffer object is mapped in the current context or any
5821 // context current to another thread, it is as though UnmapBuffer (see section
5822 // 6.3.1) is executed in each such context prior to deleting the existing data
5823 // store.
5824 // Track that the buffer was unmapped, for use during state reset
5825 mResourceTracker.setBufferUnmapped(buffer->id().value);
5826
5827 break;
5828 }
5829 case EntryPoint::GLCopyBufferSubData:
5830 {
5831 maybeCaptureCoherentBuffers(context);
5832 break;
5833 }
5834 case EntryPoint::GLDeleteFramebuffers:
5835 case EntryPoint::GLDeleteFramebuffersOES:
5836 {
5837 // Look up how many framebuffers are being deleted
5838 GLsizei n = call.params.getParam("n", ParamType::TGLsizei, 0).value.GLsizeiVal;
5839
5840 // Look up the pointer to list of framebuffers
5841 const gl::FramebufferID *framebufferIDs =
5842 call.params.getParam("framebuffersPacked", ParamType::TFramebufferIDConstPointer, 1)
5843 .value.FramebufferIDConstPointerVal;
5844
5845 // For each framebuffer listed for deletion
5846 for (int32_t i = 0; i < n; ++i)
5847 {
5848 // If we're capturing, track what framebuffers have been deleted
5849 handleDeletedResource(framebufferIDs[i]);
5850 }
5851 break;
5852 }
5853 default:
5854 break;
5855 }
5856
5857 if (IsTextureUpdate(call))
5858 {
5859 // If this call modified texture contents, track it for possible reset
5860 trackTextureUpdate(context, call);
5861 }
5862
5863 updateReadBufferSize(call.params.getReadBufferSize());
5864
5865 std::vector<gl::ShaderProgramID> shaderProgramIDs;
5866 if (FindShaderProgramIDsInCall(call, shaderProgramIDs))
5867 {
5868 for (gl::ShaderProgramID shaderProgramID : shaderProgramIDs)
5869 {
5870 mResourceTracker.onShaderProgramAccess(shaderProgramID);
5871
5872 if (isCaptureActive())
5873 {
5874 // Track that this call referenced a ShaderProgram, setting it active for Setup
5875 MarkResourceIDActive(ResourceIDType::ShaderProgram, shaderProgramID.value,
5876 shareGroupSetupCalls, resourceIDToSetupCalls);
5877 }
5878 }
5879 }
5880
5881 updateResourceCountsFromCallCapture(call);
5882 }
5883
5884 template <typename ParamValueType>
maybeGenResourceOnBind(CallCapture & call)5885 void FrameCaptureShared::maybeGenResourceOnBind(CallCapture &call)
5886 {
5887 const char *paramName = ParamValueTrait<ParamValueType>::name;
5888 const ParamType paramType = ParamValueTrait<ParamValueType>::typeID;
5889
5890 const ParamCapture ¶m = call.params.getParam(paramName, paramType, 1);
5891 const ParamValueType id = AccessParamValue<ParamValueType>(paramType, param.value);
5892
5893 // Don't inject the default resource or resources that are already generated
5894 if (id.value != 0 && !resourceIsGenerated(id))
5895 {
5896 handleGennedResource(id);
5897
5898 ResourceIDType resourceIDType = GetResourceIDTypeFromParamType(param.type);
5899 const char *resourceName = GetResourceIDTypeName(resourceIDType);
5900
5901 std::stringstream updateFuncNameStr;
5902 updateFuncNameStr << "Set" << resourceName << "ID";
5903 std::string updateFuncName = updateFuncNameStr.str();
5904
5905 ParamBuffer params;
5906 params.addValueParam("id", ParamType::TGLuint, id.value);
5907 mFrameCalls.emplace_back(updateFuncName, std::move(params));
5908 }
5909 }
5910
updateResourceCountsFromParamCapture(const ParamCapture & param,ResourceIDType idType)5911 void FrameCaptureShared::updateResourceCountsFromParamCapture(const ParamCapture ¶m,
5912 ResourceIDType idType)
5913 {
5914 if (idType != ResourceIDType::InvalidEnum)
5915 {
5916 mHasResourceType.set(idType);
5917
5918 // Capture resource IDs for non-pointer types.
5919 if (strcmp(ParamTypeToString(param.type), "GLuint") == 0)
5920 {
5921 mMaxAccessedResourceIDs[idType] =
5922 std::max(mMaxAccessedResourceIDs[idType], param.value.GLuintVal);
5923 }
5924 // Capture resource IDs for pointer types.
5925 if (strstr(ParamTypeToString(param.type), "GLuint *") != nullptr)
5926 {
5927 if (param.data.size() == 1u)
5928 {
5929 const GLuint *dataPtr = reinterpret_cast<const GLuint *>(param.data[0].data());
5930 size_t numHandles = param.data[0].size() / sizeof(GLuint);
5931 for (size_t handleIndex = 0; handleIndex < numHandles; ++handleIndex)
5932 {
5933 mMaxAccessedResourceIDs[idType] =
5934 std::max(mMaxAccessedResourceIDs[idType], dataPtr[handleIndex]);
5935 }
5936 }
5937 }
5938 }
5939 }
5940
updateResourceCountsFromCallCapture(const CallCapture & call)5941 void FrameCaptureShared::updateResourceCountsFromCallCapture(const CallCapture &call)
5942 {
5943 for (const ParamCapture ¶m : call.params.getParamCaptures())
5944 {
5945 ResourceIDType idType = GetResourceIDTypeFromParamType(param.type);
5946 updateResourceCountsFromParamCapture(param, idType);
5947 }
5948
5949 // Update resource IDs in the return value. Return values types are not stored as resource IDs,
5950 // but instead are stored as GLuints. Therefore we need to explicitly label the resource ID type
5951 // when we call update. Currently only shader and program creation are explicitly tracked.
5952 switch (call.entryPoint)
5953 {
5954 case EntryPoint::GLCreateShader:
5955 case EntryPoint::GLCreateProgram:
5956 updateResourceCountsFromParamCapture(call.params.getReturnValue(),
5957 ResourceIDType::ShaderProgram);
5958 break;
5959
5960 default:
5961 break;
5962 }
5963 }
5964
captureCall(const gl::Context * context,CallCapture && inCall,bool isCallValid)5965 void FrameCaptureShared::captureCall(const gl::Context *context,
5966 CallCapture &&inCall,
5967 bool isCallValid)
5968 {
5969 if (SkipCall(inCall.entryPoint))
5970 {
5971 return;
5972 }
5973
5974 if (isCallValid)
5975 {
5976 // If the context ID has changed, then we need to inject an eglMakeCurrent() call. Only do
5977 // this if there is more than 1 context in the share group to avoid unnecessary
5978 // eglMakeCurrent() calls.
5979 size_t contextCount = context->getShareGroup()->getShareGroupContextCount();
5980 if (contextCount > 1 && mLastContextId != context->id())
5981 {
5982 // Inject the eglMakeCurrent() call.
5983 // The EGLDisplay and EGLSurface values can't be known here, since we may not even be
5984 // running the trace with ANGLE.
5985 // The EGLContext value is actually the context ID, so we can look it up in
5986 // 'gContextMap'.
5987 // Need to go from uint32 -> uint64 -> EGLContext (void*) to handle MSVC compiler
5988 // warning on 64b systems:
5989 // error C4312: 'reinterpret_cast': conversion from 'uint32_t' to 'EGLContext' of
5990 // greater size
5991 uint64_t contextID = static_cast<uint64_t>(context->id().value);
5992 EGLContext eglContext = reinterpret_cast<EGLContext>(contextID);
5993 CallCapture makeCurrentCall =
5994 CaptureMakeCurrent(EGL_NO_DISPLAY, EGL_NO_SURFACE, EGL_NO_SURFACE, eglContext);
5995 mFrameCalls.emplace_back(std::move(makeCurrentCall));
5996 mLastContextId = context->id();
5997 }
5998
5999 std::vector<CallCapture> outCalls;
6000 maybeOverrideEntryPoint(context, inCall, outCalls);
6001
6002 // Need to loop on any new calls we added during override
6003 for (CallCapture &call : outCalls)
6004 {
6005 // During capture, consider all frame calls active
6006 if (isCaptureActive())
6007 {
6008 call.isActive = true;
6009 }
6010
6011 maybeCapturePreCallUpdates(context, call, &mShareGroupSetupCalls,
6012 &mResourceIDToSetupCalls);
6013 mFrameCalls.emplace_back(std::move(call));
6014 maybeCapturePostCallUpdates(context);
6015 }
6016
6017 // Evaluate the validation expression to determine if we insert a validation checkpoint.
6018 // This lets the user pick a subset of calls to check instead of checking every call.
6019 if (mValidateSerializedState && !mValidationExpression.empty())
6020 {
6021 // Example substitution for frame #2, call #110:
6022 // Before: (call == 2) && (frame >= 100) && (frame <= 120) && ((frame % 10) == 0)
6023 // After: (2 == 2) && (110 >= 100) && (110 <= 120) && ((110 % 10) == 0)
6024 // Evaluates to 1.0.
6025 std::string expression = mValidationExpression;
6026
6027 angle::ReplaceAllSubstrings(&expression, "frame", std::to_string(mFrameIndex));
6028 angle::ReplaceAllSubstrings(&expression, "call", std::to_string(mFrameCalls.size()));
6029
6030 double result = ceval_result(expression);
6031 if (result > 0)
6032 {
6033 CaptureValidateSerializedState(context, &mFrameCalls);
6034 }
6035 }
6036 }
6037 else
6038 {
6039 INFO() << "FrameCapture: Not capturing invalid call to "
6040 << GetEntryPointName(inCall.entryPoint);
6041 }
6042 }
6043
maybeCapturePostCallUpdates(const gl::Context * context)6044 void FrameCaptureShared::maybeCapturePostCallUpdates(const gl::Context *context)
6045 {
6046 // Process resource ID updates.
6047 if (isCaptureActive())
6048 {
6049 MaybeCaptureUpdateResourceIDs(&mResourceTracker, &mFrameCalls);
6050 }
6051
6052 CallCapture &lastCall = mFrameCalls.back();
6053 switch (lastCall.entryPoint)
6054 {
6055 case EntryPoint::GLCreateShaderProgramv:
6056 {
6057 gl::ShaderProgramID programId;
6058 programId.value = lastCall.params.getReturnValue().value.GLuintVal;
6059 const gl::Program *program = context->getProgramResolveLink(programId);
6060 CaptureUpdateUniformLocations(program, &mFrameCalls);
6061 CaptureUpdateUniformBlockIndexes(program, &mFrameCalls);
6062 break;
6063 }
6064 case EntryPoint::GLLinkProgram:
6065 {
6066 const ParamCapture ¶m =
6067 lastCall.params.getParam("programPacked", ParamType::TShaderProgramID, 0);
6068 const gl::Program *program =
6069 context->getProgramResolveLink(param.value.ShaderProgramIDVal);
6070 CaptureUpdateUniformLocations(program, &mFrameCalls);
6071 CaptureUpdateUniformBlockIndexes(program, &mFrameCalls);
6072 break;
6073 }
6074 case EntryPoint::GLUseProgram:
6075 CaptureUpdateCurrentProgram(lastCall, 0, &mFrameCalls);
6076 break;
6077 case EntryPoint::GLActiveShaderProgram:
6078 CaptureUpdateCurrentProgram(lastCall, 1, &mFrameCalls);
6079 break;
6080 case EntryPoint::GLDeleteProgram:
6081 {
6082 const ParamCapture ¶m =
6083 lastCall.params.getParam("programPacked", ParamType::TShaderProgramID, 0);
6084 CaptureDeleteUniformLocations(param.value.ShaderProgramIDVal, &mFrameCalls);
6085 break;
6086 }
6087 case EntryPoint::GLShaderSource:
6088 {
6089 lastCall.params.setValueParamAtIndex("count", ParamType::TGLsizei, 1, 1);
6090
6091 ParamCapture ¶mLength =
6092 lastCall.params.getParam("length", ParamType::TGLintConstPointer, 3);
6093 paramLength.data.resize(1);
6094 // Set the length parameter to {-1} to signal that the actual string length
6095 // is to be used. Since we store the parameter blob as an array of four uint8_t
6096 // values, we have to pass the binary equivalent of -1.
6097 paramLength.data[0] = {0xff, 0xff, 0xff, 0xff};
6098 break;
6099 }
6100 default:
6101 break;
6102 }
6103 }
6104
captureClientArraySnapshot(const gl::Context * context,size_t vertexCount,size_t instanceCount)6105 void FrameCaptureShared::captureClientArraySnapshot(const gl::Context *context,
6106 size_t vertexCount,
6107 size_t instanceCount)
6108 {
6109 const gl::VertexArray *vao = context->getState().getVertexArray();
6110
6111 // Capture client array data.
6112 for (size_t attribIndex : context->getStateCache().getActiveClientAttribsMask())
6113 {
6114 const gl::VertexAttribute &attrib = vao->getVertexAttribute(attribIndex);
6115 const gl::VertexBinding &binding = vao->getVertexBinding(attrib.bindingIndex);
6116
6117 int callIndex = mClientVertexArrayMap[attribIndex];
6118
6119 if (callIndex != -1)
6120 {
6121 size_t count = vertexCount;
6122
6123 if (binding.getDivisor() > 0)
6124 {
6125 count = rx::UnsignedCeilDivide(static_cast<uint32_t>(instanceCount),
6126 binding.getDivisor());
6127 }
6128
6129 // The last capture element doesn't take up the full stride.
6130 size_t bytesToCapture = (count - 1) * binding.getStride() + attrib.format->pixelBytes;
6131
6132 CallCapture &call = mFrameCalls[callIndex];
6133 ParamCapture ¶m = call.params.getClientArrayPointerParameter();
6134 ASSERT(param.type == ParamType::TvoidConstPointer);
6135
6136 ParamBuffer updateParamBuffer;
6137 updateParamBuffer.addValueParam<GLint>("arrayIndex", ParamType::TGLint,
6138 static_cast<uint32_t>(attribIndex));
6139
6140 ParamCapture updateMemory("pointer", ParamType::TvoidConstPointer);
6141 CaptureMemory(param.value.voidConstPointerVal, bytesToCapture, &updateMemory);
6142 updateParamBuffer.addParam(std::move(updateMemory));
6143
6144 updateParamBuffer.addValueParam<GLuint64>("size", ParamType::TGLuint64, bytesToCapture);
6145
6146 mFrameCalls.emplace_back("UpdateClientArrayPointer", std::move(updateParamBuffer));
6147
6148 mClientArraySizes[attribIndex] =
6149 std::max(mClientArraySizes[attribIndex], bytesToCapture);
6150 }
6151 }
6152 }
6153
captureCoherentBufferSnapshot(const gl::Context * context,gl::BufferID id)6154 void FrameCaptureShared::captureCoherentBufferSnapshot(const gl::Context *context, gl::BufferID id)
6155 {
6156 if (!hasBufferData(id))
6157 {
6158 // This buffer was not marked writable
6159 return;
6160 }
6161
6162 const gl::State &apiState = context->getState();
6163 const gl::BufferManager &buffers = apiState.getBufferManagerForCapture();
6164 gl::Buffer *buffer = buffers.getBuffer(id);
6165 if (!buffer)
6166 {
6167 // Could not find buffer binding
6168 return;
6169 }
6170
6171 ASSERT(buffer->isMapped());
6172
6173 std::shared_ptr<angle::CoherentBuffer> coherentBuffer =
6174 mCoherentBufferTracker.mBuffers[id.value];
6175
6176 std::vector<PageRange> dirtyPageRanges = coherentBuffer->getDirtyPageRanges();
6177
6178 AddressRange wholeRange = coherentBuffer->getRange();
6179
6180 for (PageRange &pageRange : dirtyPageRanges)
6181 {
6182 // Write protect the memory already, so the app is blocked on writing during our capture
6183 coherentBuffer->protectPageRange(pageRange);
6184
6185 // Create the parameters to our helper for use during replay
6186 ParamBuffer dataParamBuffer;
6187
6188 // Pass in the target buffer ID
6189 dataParamBuffer.addValueParam("dest", ParamType::TGLuint, buffer->id().value);
6190
6191 // Capture the current buffer data with a binary param
6192 ParamCapture captureData("source", ParamType::TvoidConstPointer);
6193
6194 AddressRange dirtyRange = coherentBuffer->getDirtyAddressRange(pageRange);
6195 CaptureMemory(reinterpret_cast<void *>(dirtyRange.start), dirtyRange.size, &captureData);
6196 dataParamBuffer.addParam(std::move(captureData));
6197
6198 // Also track its size for use with memcpy
6199 dataParamBuffer.addValueParam<GLsizeiptr>("size", ParamType::TGLsizeiptr,
6200 static_cast<GLsizeiptr>(dirtyRange.size));
6201
6202 if (wholeRange.start != dirtyRange.start)
6203 {
6204 // Capture with offset
6205 GLsizeiptr offset = dirtyRange.start - wholeRange.start;
6206
6207 ASSERT(offset > 0);
6208
6209 // The dirty page range is not at the start of the buffer, track the offset.
6210 dataParamBuffer.addValueParam<GLsizeiptr>("offset", ParamType::TGLsizeiptr, offset);
6211
6212 // Call the helper that populates the buffer with captured data
6213 mFrameCalls.emplace_back("UpdateClientBufferDataWithOffset",
6214 std::move(dataParamBuffer));
6215 }
6216 else
6217 {
6218 // Call the helper that populates the buffer with captured data
6219 mFrameCalls.emplace_back("UpdateClientBufferData", std::move(dataParamBuffer));
6220 }
6221 }
6222 }
6223
captureMappedBufferSnapshot(const gl::Context * context,const CallCapture & call)6224 void FrameCaptureShared::captureMappedBufferSnapshot(const gl::Context *context,
6225 const CallCapture &call)
6226 {
6227 // If the buffer was mapped writable, we need to restore its data, since we have no
6228 // visibility into what the client did to the buffer while mapped.
6229 // This sequence will result in replay calls like this:
6230 // ...
6231 // gMappedBufferData[gBufferMap[42]] = glMapBufferRange(GL_PIXEL_UNPACK_BUFFER, 0, 65536,
6232 // GL_MAP_WRITE_BIT);
6233 // ...
6234 // UpdateClientBufferData(42, &gBinaryData[164631024], 65536);
6235 // glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
6236 // ...
6237
6238 // Re-map the buffer, using the info we tracked about the buffer
6239 gl::BufferBinding target =
6240 call.params.getParam("targetPacked", ParamType::TBufferBinding, 0).value.BufferBindingVal;
6241
6242 FrameCaptureShared *frameCaptureShared = context->getShareGroup()->getFrameCaptureShared();
6243 gl::Buffer *buffer = context->getState().getTargetBuffer(target);
6244 if (!frameCaptureShared->hasBufferData(buffer->id()))
6245 {
6246 // This buffer was not marked writable, so we did not back it up
6247 return;
6248 }
6249
6250 std::pair<GLintptr, GLsizeiptr> bufferDataOffsetAndLength =
6251 frameCaptureShared->getBufferDataOffsetAndLength(buffer->id());
6252 GLintptr offset = bufferDataOffsetAndLength.first;
6253 GLsizeiptr length = bufferDataOffsetAndLength.second;
6254
6255 // Map the buffer so we can copy its contents out
6256 ASSERT(!buffer->isMapped());
6257 angle::Result result = buffer->mapRange(context, offset, length, GL_MAP_READ_BIT);
6258 if (result != angle::Result::Continue)
6259 {
6260 ERR() << "Failed to mapRange of buffer" << std::endl;
6261 }
6262 const uint8_t *data = reinterpret_cast<const uint8_t *>(buffer->getMapPointer());
6263
6264 // Create the parameters to our helper for use during replay
6265 ParamBuffer dataParamBuffer;
6266
6267 // Pass in the target buffer ID
6268 dataParamBuffer.addValueParam("dest", ParamType::TGLuint, buffer->id().value);
6269
6270 // Capture the current buffer data with a binary param
6271 ParamCapture captureData("source", ParamType::TvoidConstPointer);
6272 CaptureMemory(data, length, &captureData);
6273 dataParamBuffer.addParam(std::move(captureData));
6274
6275 // Also track its size for use with memcpy
6276 dataParamBuffer.addValueParam<GLsizeiptr>("size", ParamType::TGLsizeiptr, length);
6277
6278 // Call the helper that populates the buffer with captured data
6279 mFrameCalls.emplace_back("UpdateClientBufferData", std::move(dataParamBuffer));
6280
6281 // Unmap the buffer and move on
6282 GLboolean dontCare;
6283 (void)buffer->unmap(context, &dontCare);
6284 }
6285
checkForCaptureTrigger()6286 void FrameCaptureShared::checkForCaptureTrigger()
6287 {
6288 // If the capture trigger has not been set, move on
6289 if (mCaptureTrigger == 0)
6290 {
6291 return;
6292 }
6293
6294 // Otherwise, poll the value for a change
6295 std::string captureTriggerStr = GetCaptureTrigger();
6296 if (captureTriggerStr.empty())
6297 {
6298 return;
6299 }
6300
6301 // If the value has changed, use the original value as the frame count
6302 // TODO (anglebug.com/4949): Improve capture at unknown frame time. It is good to
6303 // avoid polling if the feature is not enabled, but not entirely intuitive to set
6304 // a value to zero when you want to trigger it.
6305 uint32_t captureTrigger = atoi(captureTriggerStr.c_str());
6306 if (captureTrigger != mCaptureTrigger)
6307 {
6308 // Start mid-execution capture for the current frame
6309 mCaptureStartFrame = mFrameIndex + 1;
6310
6311 // Use the original trigger value as the frame count
6312 mCaptureEndFrame = mCaptureStartFrame + mCaptureTrigger - 1;
6313
6314 INFO() << "Capture triggered after frame " << mFrameIndex << " for " << mCaptureTrigger
6315 << " frames";
6316
6317 // Stop polling
6318 mCaptureTrigger = 0;
6319 }
6320 }
6321
scanSetupCalls(const gl::Context * context,std::vector<CallCapture> & setupCalls)6322 void FrameCaptureShared::scanSetupCalls(const gl::Context *context,
6323 std::vector<CallCapture> &setupCalls)
6324 {
6325 // Scan all the instructions in the list for tracking
6326 for (CallCapture &call : setupCalls)
6327 {
6328 updateReadBufferSize(call.params.getReadBufferSize());
6329 updateResourceCountsFromCallCapture(call);
6330 }
6331 }
6332
runMidExecutionCapture(const gl::Context * mainContext)6333 void FrameCaptureShared::runMidExecutionCapture(const gl::Context *mainContext)
6334 {
6335 mMidExecutionCaptureActive = true;
6336
6337 // Make sure all pending work for every Context in the share group has completed so all data
6338 // (buffers, textures, etc.) has been updated and no resources are in use.
6339 egl::ShareGroup *shareGroup = mainContext->getShareGroup();
6340 shareGroup->finishAllContexts();
6341
6342 const gl::State &contextState = mainContext->getState();
6343 gl::State mainContextReplayState(nullptr, nullptr, nullptr, nullptr, nullptr, EGL_OPENGL_ES_API,
6344 contextState.getClientVersion(), false, true, true, true,
6345 false, EGL_CONTEXT_PRIORITY_MEDIUM_IMG,
6346 contextState.hasProtectedContent());
6347 mainContextReplayState.initializeForCapture(mainContext);
6348
6349 CaptureShareGroupMidExecutionSetup(mainContext, &mShareGroupSetupCalls, &mResourceTracker,
6350 mainContextReplayState);
6351
6352 scanSetupCalls(mainContext, mShareGroupSetupCalls);
6353
6354 for (const gl::Context *shareContext : shareGroup->getContexts())
6355 {
6356 FrameCapture *frameCapture = shareContext->getFrameCapture();
6357 ASSERT(frameCapture->getSetupCalls().empty());
6358
6359 if (shareContext->id() == mainContext->id())
6360 {
6361 CaptureMidExecutionSetup(shareContext, &frameCapture->getSetupCalls(),
6362 &mShareGroupSetupCalls, &mResourceIDToSetupCalls,
6363 &mResourceTracker, mainContextReplayState,
6364 mValidateSerializedState);
6365 scanSetupCalls(mainContext, frameCapture->getSetupCalls());
6366 }
6367 else
6368 {
6369 gl::State auxContextReplayState(
6370 nullptr, nullptr, nullptr, nullptr, nullptr, EGL_OPENGL_ES_API,
6371 shareContext->getState().getClientVersion(), false, true, true, true, false,
6372 EGL_CONTEXT_PRIORITY_MEDIUM_IMG, shareContext->getState().hasProtectedContent());
6373 auxContextReplayState.initializeForCapture(shareContext);
6374
6375 CaptureMidExecutionSetup(shareContext, &frameCapture->getSetupCalls(),
6376 &mShareGroupSetupCalls, &mResourceIDToSetupCalls,
6377 &mResourceTracker, auxContextReplayState,
6378 mValidateSerializedState);
6379
6380 scanSetupCalls(mainContext, frameCapture->getSetupCalls());
6381
6382 WriteAuxiliaryContextCppSetupReplay(
6383 mReplayWriter, mCompression, mOutDirectory, shareContext, mCaptureLabel, 1,
6384 frameCapture->getSetupCalls(), &mBinaryData, mSerializeStateEnabled, *this);
6385 }
6386 }
6387
6388 mMidExecutionCaptureActive = false;
6389 }
6390
onEndFrame(const gl::Context * context)6391 void FrameCaptureShared::onEndFrame(const gl::Context *context)
6392 {
6393 if (!enabled() || mFrameIndex > mCaptureEndFrame)
6394 {
6395 setCaptureInactive();
6396 mCoherentBufferTracker.onEndFrame();
6397 return;
6398 }
6399
6400 FrameCapture *frameCapture = context->getFrameCapture();
6401
6402 // Count resource IDs. This is also done on every frame. It could probably be done by
6403 // checking the GL state instead of the calls.
6404 for (const CallCapture &call : mFrameCalls)
6405 {
6406 for (const ParamCapture ¶m : call.params.getParamCaptures())
6407 {
6408 ResourceIDType idType = GetResourceIDTypeFromParamType(param.type);
6409 if (idType != ResourceIDType::InvalidEnum)
6410 {
6411 mHasResourceType.set(idType);
6412 }
6413 }
6414 }
6415
6416 // Assume that the context performing the swap is the "main" context.
6417 ASSERT(mWindowSurfaceContextID.value == 0 || mWindowSurfaceContextID == context->id());
6418 mWindowSurfaceContextID = context->id();
6419
6420 // On Android, we can trigger a capture during the run
6421 checkForCaptureTrigger();
6422
6423 // Check for MEC. Done after checkForCaptureTrigger(), since that can modify mCaptureStartFrame.
6424 if (mFrameIndex < mCaptureStartFrame)
6425 {
6426 if (mFrameIndex == mCaptureStartFrame - 1)
6427 {
6428 runMidExecutionCapture(context);
6429
6430 // Set the capture active to ensure all GLES commands issued by the next frame are
6431 // handled correctly by maybeCapturePreCallUpdates() and maybeCapturePostCallUpdates().
6432 setCaptureActive();
6433 }
6434 mFrameIndex++;
6435 reset();
6436 return;
6437 }
6438
6439 ASSERT(isCaptureActive());
6440
6441 if (!mFrameCalls.empty())
6442 {
6443 mActiveFrameIndices.push_back(getReplayFrameIndex());
6444 }
6445
6446 // Make sure all pending work for every Context in the share group has completed so all data
6447 // (buffers, textures, etc.) has been updated and no resources are in use.
6448 egl::ShareGroup *shareGroup = context->getShareGroup();
6449 shareGroup->finishAllContexts();
6450
6451 // Only validate the first frame for now to save on retracing time.
6452 if (mValidateSerializedState && mFrameIndex == mCaptureStartFrame)
6453 {
6454 CaptureValidateSerializedState(context, &mFrameCalls);
6455 }
6456
6457 writeMainContextCppReplay(context, frameCapture->getSetupCalls());
6458
6459 if (mFrameIndex == mCaptureEndFrame)
6460 {
6461 // Write shared MEC after frame sequence so we can eliminate unused assets like programs
6462 WriteShareGroupCppSetupReplay(mReplayWriter, mCompression, mOutDirectory, mCaptureLabel, 1,
6463 1, mShareGroupSetupCalls, &mResourceTracker, &mBinaryData,
6464 mSerializeStateEnabled, mWindowSurfaceContextID);
6465
6466 // Save the index files after the last frame.
6467 writeCppReplayIndexFiles(context, false);
6468 SaveBinaryData(mCompression, mOutDirectory, kSharedContextId, mCaptureLabel, mBinaryData);
6469 mBinaryData.clear();
6470 mWroteIndexFile = true;
6471 }
6472
6473 reset();
6474 mFrameIndex++;
6475 }
6476
onDestroyContext(const gl::Context * context)6477 void FrameCaptureShared::onDestroyContext(const gl::Context *context)
6478 {
6479 if (!mEnabled)
6480 {
6481 return;
6482 }
6483 if (!mWroteIndexFile && mFrameIndex > mCaptureStartFrame)
6484 {
6485 // If context is destroyed before end frame is reached and at least
6486 // 1 frame has been recorded, then write the index files.
6487 // It doesn't make sense to write the index files when no frame has been recorded
6488 mFrameIndex -= 1;
6489 mCaptureEndFrame = mFrameIndex;
6490 writeCppReplayIndexFiles(context, true);
6491 SaveBinaryData(mCompression, mOutDirectory, kSharedContextId, mCaptureLabel, mBinaryData);
6492 mBinaryData.clear();
6493 mWroteIndexFile = true;
6494 }
6495 }
6496
onMakeCurrent(const gl::Context * context,const egl::Surface * drawSurface)6497 void FrameCaptureShared::onMakeCurrent(const gl::Context *context, const egl::Surface *drawSurface)
6498 {
6499 if (!drawSurface)
6500 {
6501 return;
6502 }
6503
6504 // Track the width, height and color space of the draw surface as provided to makeCurrent
6505 SurfaceParams ¶ms = mDrawSurfaceParams[context->id()];
6506 params.extents = gl::Extents(drawSurface->getWidth(), drawSurface->getHeight(), 1);
6507 params.colorSpace = egl::FromEGLenum<egl::ColorSpace>(drawSurface->getGLColorspace());
6508 }
6509
6510 DataCounters::DataCounters() = default;
6511
6512 DataCounters::~DataCounters() = default;
6513
getAndIncrement(EntryPoint entryPoint,const std::string & paramName)6514 int DataCounters::getAndIncrement(EntryPoint entryPoint, const std::string ¶mName)
6515 {
6516 Counter counterKey = {entryPoint, paramName};
6517 return mData[counterKey]++;
6518 }
6519
6520 DataTracker::DataTracker() = default;
6521
6522 DataTracker::~DataTracker() = default;
6523
6524 StringCounters::StringCounters() = default;
6525
6526 StringCounters::~StringCounters() = default;
6527
getStringCounter(const std::vector<std::string> & strings)6528 int StringCounters::getStringCounter(const std::vector<std::string> &strings)
6529 {
6530 const auto &id = mStringCounterMap.find(strings);
6531 if (id == mStringCounterMap.end())
6532 {
6533 return kStringsNotFound;
6534 }
6535 else
6536 {
6537 return mStringCounterMap[strings];
6538 }
6539 }
6540
setStringCounter(const std::vector<std::string> & strings,int & counter)6541 void StringCounters::setStringCounter(const std::vector<std::string> &strings, int &counter)
6542 {
6543 ASSERT(counter >= 0);
6544 mStringCounterMap[strings] = counter;
6545 }
6546
6547 TrackedResource::TrackedResource() = default;
6548
6549 TrackedResource::~TrackedResource() = default;
6550
6551 ResourceTracker::ResourceTracker() = default;
6552
6553 ResourceTracker::~ResourceTracker() = default;
6554
setDeletedFenceSync(GLsync sync)6555 void ResourceTracker::setDeletedFenceSync(GLsync sync)
6556 {
6557 ASSERT(sync != nullptr);
6558 if (mStartingFenceSyncs.find(sync) == mStartingFenceSyncs.end())
6559 {
6560 // This is a fence sync created after MEC was initialized. Ignore it.
6561 return;
6562 }
6563
6564 // In this case, the app is deleting a fence sync we started with, we need to regen on loop.
6565 mFenceSyncsToRegen.insert(sync);
6566 }
6567
setGennedResource(GLuint id)6568 void TrackedResource::setGennedResource(GLuint id)
6569 {
6570 if (mStartingResources.find(id) == mStartingResources.end())
6571 {
6572 // This is a resource created after MEC was initialized, track it
6573 mNewResources.insert(id);
6574 return;
6575 }
6576 }
6577
resourceIsGenerated(GLuint id)6578 bool TrackedResource::resourceIsGenerated(GLuint id)
6579 {
6580 return mStartingResources.find(id) != mStartingResources.end() ||
6581 mNewResources.find(id) != mNewResources.end();
6582 }
6583
setDeletedResource(GLuint id)6584 void TrackedResource::setDeletedResource(GLuint id)
6585 {
6586 if (id == 0)
6587 {
6588 // Ignore ID 0
6589 return;
6590 }
6591
6592 if (mNewResources.find(id) != mNewResources.end())
6593 {
6594 // This is a resource created after MEC was initialized, just clear it, since there will be
6595 // no actions required for it to return to starting state.
6596 mNewResources.erase(id);
6597 return;
6598 }
6599
6600 if (mStartingResources.find(id) != mStartingResources.end())
6601 {
6602 // In this case, the app is deleting a resource we started with, we need to regen on loop
6603 mResourcesToRegen.insert(id);
6604
6605 // Also restore its contents
6606 mResourcesToRestore.insert(id);
6607 }
6608
6609 // If none of the above is true, the app is deleting a resource that was never genned.
6610 }
6611
setModifiedResource(GLuint id)6612 void TrackedResource::setModifiedResource(GLuint id)
6613 {
6614 // If this was a starting resource, we need to track it for restore
6615 if (mStartingResources.find(id) != mStartingResources.end())
6616 {
6617 mResourcesToRestore.insert(id);
6618 }
6619 }
6620
setBufferMapped(GLuint id)6621 void ResourceTracker::setBufferMapped(GLuint id)
6622 {
6623 // If this was a starting buffer, we may need to restore it to original state during Reset.
6624 // Skip buffers that were deleted after the starting point.
6625 const TrackedResource &trackedBuffers = getTrackedResource(ResourceIDType::Buffer);
6626 const ResourceSet &startingBuffers = trackedBuffers.getStartingResources();
6627 const ResourceSet &buffersToRegen = trackedBuffers.getResourcesToRegen();
6628 if (startingBuffers.find(id) != startingBuffers.end() &&
6629 buffersToRegen.find(id) == buffersToRegen.end())
6630 {
6631 // Track that its current state is mapped (true)
6632 mStartingBuffersMappedCurrent[id] = true;
6633 }
6634 }
6635
setBufferUnmapped(GLuint id)6636 void ResourceTracker::setBufferUnmapped(GLuint id)
6637 {
6638 // If this was a starting buffer, we may need to restore it to original state during Reset.
6639 // Skip buffers that were deleted after the starting point.
6640 const TrackedResource &trackedBuffers = getTrackedResource(ResourceIDType::Buffer);
6641 const ResourceSet &startingBuffers = trackedBuffers.getStartingResources();
6642 const ResourceSet &buffersToRegen = trackedBuffers.getResourcesToRegen();
6643 if (startingBuffers.find(id) != startingBuffers.end() &&
6644 buffersToRegen.find(id) == buffersToRegen.end())
6645 {
6646 // Track that its current state is unmapped (false)
6647 mStartingBuffersMappedCurrent[id] = false;
6648 }
6649 }
6650
getStartingBuffersMappedCurrent(GLuint id) const6651 bool ResourceTracker::getStartingBuffersMappedCurrent(GLuint id) const
6652 {
6653 const auto &foundBool = mStartingBuffersMappedCurrent.find(id);
6654 ASSERT(foundBool != mStartingBuffersMappedCurrent.end());
6655 return foundBool->second;
6656 }
6657
getStartingBuffersMappedInitial(GLuint id) const6658 bool ResourceTracker::getStartingBuffersMappedInitial(GLuint id) const
6659 {
6660 const auto &foundBool = mStartingBuffersMappedInitial.find(id);
6661 ASSERT(foundBool != mStartingBuffersMappedInitial.end());
6662 return foundBool->second;
6663 }
6664
onShaderProgramAccess(gl::ShaderProgramID shaderProgramID)6665 void ResourceTracker::onShaderProgramAccess(gl::ShaderProgramID shaderProgramID)
6666 {
6667 mMaxShaderPrograms = std::max(mMaxShaderPrograms, shaderProgramID.value + 1);
6668 }
6669
isCapturing() const6670 bool FrameCaptureShared::isCapturing() const
6671 {
6672 // Currently we will always do a capture up until the last frame. In the future we could improve
6673 // mid execution capture by only capturing between the start and end frames. The only necessary
6674 // reason we need to capture before the start is for attached program and shader sources.
6675 return mEnabled && mFrameIndex <= mCaptureEndFrame;
6676 }
6677
getFrameCount() const6678 uint32_t FrameCaptureShared::getFrameCount() const
6679 {
6680 return mCaptureEndFrame - mCaptureStartFrame + 1;
6681 }
6682
getReplayFrameIndex() const6683 uint32_t FrameCaptureShared::getReplayFrameIndex() const
6684 {
6685 return mFrameIndex - mCaptureStartFrame + 1;
6686 }
6687
replay(gl::Context * context)6688 void FrameCaptureShared::replay(gl::Context *context)
6689 {
6690 ReplayContext replayContext(mReadBufferSize, mClientArraySizes);
6691 for (const CallCapture &call : mFrameCalls)
6692 {
6693 INFO() << "frame index: " << mFrameIndex << " " << call.name();
6694
6695 if (call.entryPoint == EntryPoint::GLInvalid)
6696 {
6697 if (call.customFunctionName == "UpdateClientArrayPointer")
6698 {
6699 GLint arrayIndex =
6700 call.params.getParam("arrayIndex", ParamType::TGLint, 0).value.GLintVal;
6701 ASSERT(arrayIndex < gl::MAX_VERTEX_ATTRIBS);
6702
6703 const ParamCapture &pointerParam =
6704 call.params.getParam("pointer", ParamType::TvoidConstPointer, 1);
6705 ASSERT(pointerParam.data.size() == 1);
6706 const void *pointer = pointerParam.data[0].data();
6707
6708 size_t size = static_cast<size_t>(
6709 call.params.getParam("size", ParamType::TGLuint64, 2).value.GLuint64Val);
6710
6711 std::vector<uint8_t> &curClientArrayBuffer =
6712 replayContext.getClientArraysBuffer()[arrayIndex];
6713 ASSERT(curClientArrayBuffer.size() >= size);
6714 memcpy(curClientArrayBuffer.data(), pointer, size);
6715 }
6716 continue;
6717 }
6718
6719 ReplayCall(context, &replayContext, call);
6720 }
6721 }
6722
6723 // Serialize trace metadata into a JSON file. The JSON file will be named "trace_prefix.json".
6724 //
6725 // As of writing, it will have the format like so:
6726 // {
6727 // "TraceMetadata":
6728 // {
6729 // "AreClientArraysEnabled" : 1, "CaptureRevision" : 16631, "ConfigAlphaBits" : 8,
6730 // "ConfigBlueBits" : 8, "ConfigDepthBits" : 24, "ConfigGreenBits" : 8,
6731 // ... etc ...
writeJSON(const gl::Context * context)6732 void FrameCaptureShared::writeJSON(const gl::Context *context)
6733 {
6734 const gl::ContextID contextId = context->id();
6735 const SurfaceParams &surfaceParams = mDrawSurfaceParams.at(contextId);
6736 const gl::State &glState = context->getState();
6737 const egl::Config *config = context->getConfig();
6738 const egl::AttributeMap &displayAttribs = context->getDisplay()->getAttributeMap();
6739
6740 unsigned int frameCount = getFrameCount();
6741
6742 JsonSerializer json;
6743 json.startGroup("TraceMetadata");
6744 json.addScalar("CaptureRevision", GetANGLERevision());
6745 json.addScalar("ContextClientMajorVersion", context->getClientMajorVersion());
6746 json.addScalar("ContextClientMinorVersion", context->getClientMinorVersion());
6747 json.addHexValue("DisplayPlatformType", displayAttribs.getAsInt(EGL_PLATFORM_ANGLE_TYPE_ANGLE));
6748 json.addHexValue("DisplayDeviceType",
6749 displayAttribs.getAsInt(EGL_PLATFORM_ANGLE_DEVICE_TYPE_ANGLE));
6750 json.addScalar("FrameStart", 1);
6751 json.addScalar("FrameEnd", frameCount);
6752 json.addScalar("DrawSurfaceWidth", surfaceParams.extents.width);
6753 json.addScalar("DrawSurfaceHeight", surfaceParams.extents.height);
6754 json.addHexValue("DrawSurfaceColorSpace", ToEGLenum(surfaceParams.colorSpace));
6755 if (config)
6756 {
6757 json.addScalar("ConfigRedBits", config->redSize);
6758 json.addScalar("ConfigGreenBits", config->greenSize);
6759 json.addScalar("ConfigBlueBits", config->blueSize);
6760 json.addScalar("ConfigAlphaBits", config->alphaSize);
6761 json.addScalar("ConfigDepthBits", config->depthSize);
6762 json.addScalar("ConfigStencilBits", config->stencilSize);
6763 }
6764 else
6765 {
6766 json.addScalar("ConfigRedBits", EGL_DONT_CARE);
6767 json.addScalar("ConfigGreenBits", EGL_DONT_CARE);
6768 json.addScalar("ConfigBlueBits", EGL_DONT_CARE);
6769 json.addScalar("ConfigAlphaBits", EGL_DONT_CARE);
6770 json.addScalar("ConfigDepthBits", EGL_DONT_CARE);
6771 json.addScalar("ConfigStencilBits", EGL_DONT_CARE);
6772 }
6773 json.addBool("IsBinaryDataCompressed", mCompression);
6774 json.addBool("AreClientArraysEnabled", glState.areClientArraysEnabled());
6775 json.addBool("IsBindGeneratesResourcesEnabled", glState.isBindGeneratesResourceEnabled());
6776 json.addBool("IsWebGLCompatibilityEnabled", glState.isWebGL());
6777 json.addBool("IsRobustResourceInitEnabled", glState.isRobustResourceInitEnabled());
6778 json.addBool("IsTrimmingEnabled", mTrimEnabled);
6779 json.endGroup();
6780
6781 {
6782 const std::vector<std::string> &traceFiles = mReplayWriter.getAndResetWrittenFiles();
6783 json.addVectorOfStrings("TraceFiles", traceFiles);
6784 }
6785
6786 json.addScalar("WindowSurfaceContextID", contextId.value);
6787
6788 {
6789 std::stringstream jsonFileNameStream;
6790 jsonFileNameStream << mOutDirectory << FmtCapturePrefix(kNoContextId, mCaptureLabel)
6791 << ".json";
6792 std::string jsonFileName = jsonFileNameStream.str();
6793
6794 SaveFileHelper saveData(jsonFileName);
6795 saveData.write(reinterpret_cast<const uint8_t *>(json.data()), json.length());
6796 }
6797 }
6798
writeCppReplayIndexFiles(const gl::Context * context,bool writeResetContextCall)6799 void FrameCaptureShared::writeCppReplayIndexFiles(const gl::Context *context,
6800 bool writeResetContextCall)
6801 {
6802 // Ensure the last frame is written. This will no-op if the frame is already written.
6803 mReplayWriter.saveFrame();
6804
6805 const gl::ContextID contextId = context->id();
6806
6807 {
6808 std::stringstream header;
6809
6810 header << "#pragma once\n";
6811 header << "\n";
6812 header << "#include <EGL/egl.h>\n";
6813 header << "#include <cstdint>\n";
6814
6815 std::string includes = header.str();
6816 mReplayWriter.setHeaderPrologue(includes);
6817 }
6818
6819 {
6820 std::stringstream source;
6821
6822 source << "#include \"" << FmtCapturePrefix(contextId, mCaptureLabel) << ".h\"\n";
6823 source << "#include \"trace_fixture.h\"\n";
6824 source << "#include \"angle_trace_gl.h\"\n";
6825
6826 std::string sourcePrologue = source.str();
6827 mReplayWriter.setSourcePrologue(sourcePrologue);
6828 }
6829
6830 {
6831 std::string proto = "void InitReplay()";
6832
6833 std::stringstream source;
6834 source << proto << "\n";
6835 source << "{\n";
6836 WriteInitReplayCall(mCompression, source, kSharedContextId, mCaptureLabel,
6837 MaxClientArraySize(mClientArraySizes), mReadBufferSize,
6838 mMaxAccessedResourceIDs);
6839 source << "}\n";
6840
6841 mReplayWriter.addPrivateFunction(proto, std::stringstream(), source);
6842 }
6843
6844 {
6845 std::string proto = "void ReplayFrame(uint32_t frameIndex)";
6846
6847 std::stringstream source;
6848
6849 source << proto << "\n";
6850 source << "{\n";
6851 source << " switch (frameIndex)\n";
6852 source << " {\n";
6853 for (uint32_t frameIndex : mActiveFrameIndices)
6854 {
6855 source << " case " << frameIndex << ":\n";
6856 source << " " << FmtReplayFunction(contextId, frameIndex) << ";\n";
6857 source << " break;\n";
6858 }
6859 source << " default:\n";
6860 source << " break;\n";
6861 source << " }\n";
6862 source << "}\n";
6863
6864 mReplayWriter.addPublicFunction(proto, std::stringstream(), source);
6865 }
6866
6867 if (writeResetContextCall)
6868 {
6869 std::string proto = "void ResetReplay()";
6870
6871 std::stringstream source;
6872
6873 source << proto << "\n";
6874 source << "{\n";
6875 source << " // Reset context is empty because context is destroyed before end "
6876 "frame is reached\n";
6877 source << "}\n";
6878
6879 mReplayWriter.addPublicFunction(proto, std::stringstream(), source);
6880 }
6881
6882 if (mSerializeStateEnabled)
6883 {
6884 std::string proto = "const char *GetSerializedContextState(uint32_t frameIndex)";
6885
6886 std::stringstream source;
6887
6888 source << proto << "\n";
6889 source << "{\n";
6890 source << " switch (frameIndex)\n";
6891 source << " {\n";
6892 for (uint32_t frameIndex = 1; frameIndex <= getFrameCount(); ++frameIndex)
6893 {
6894 source << " case " << frameIndex << ":\n";
6895 source << " return "
6896 << FmtGetSerializedContextStateFunction(contextId, frameIndex) << ";\n";
6897 }
6898 source << " default:\n";
6899 source << " return nullptr;\n";
6900 source << " }\n";
6901 source << "}\n";
6902
6903 mReplayWriter.addPublicFunction(proto, std::stringstream(), source);
6904 }
6905
6906 {
6907 std::stringstream fnameStream;
6908 fnameStream << mOutDirectory << FmtCapturePrefix(contextId, mCaptureLabel);
6909 std::string fnamePattern = fnameStream.str();
6910
6911 mReplayWriter.setFilenamePattern(fnamePattern);
6912 }
6913
6914 mReplayWriter.saveIndexFilesAndHeader();
6915
6916 writeJSON(context);
6917 }
6918
writeMainContextCppReplay(const gl::Context * context,const std::vector<CallCapture> & setupCalls)6919 void FrameCaptureShared::writeMainContextCppReplay(const gl::Context *context,
6920 const std::vector<CallCapture> &setupCalls)
6921 {
6922 ASSERT(mWindowSurfaceContextID == context->id());
6923
6924 {
6925 std::stringstream header;
6926
6927 header << "#include \"" << FmtCapturePrefix(context->id(), mCaptureLabel) << ".h\"\n";
6928 header << "#include \"angle_trace_gl.h\"\n";
6929
6930 std::string headerString = header.str();
6931 mReplayWriter.setSourcePrologue(headerString);
6932 }
6933
6934 uint32_t frameCount = getFrameCount();
6935 uint32_t frameIndex = getReplayFrameIndex();
6936
6937 if (frameIndex == 1)
6938 {
6939 {
6940 std::stringstream protoStream;
6941 std::stringstream headerStream;
6942 std::stringstream bodyStream;
6943
6944 protoStream << "void " << FmtSetupFunction(kNoPartId, context->id());
6945 std::string proto = protoStream.str();
6946
6947 WriteCppReplayFunctionWithParts(context->id(), ReplayFunc::Setup, mReplayWriter,
6948 frameIndex, &mBinaryData, setupCalls, headerStream,
6949 bodyStream);
6950
6951 mReplayWriter.addPrivateFunction(proto, headerStream, bodyStream);
6952 }
6953
6954 {
6955 std::string proto = "void SetupReplay()";
6956
6957 std::stringstream out;
6958
6959 out << proto << "\n";
6960 out << "{\n";
6961 out << " EGLContext context = eglGetCurrentContext();\n";
6962 out << " gContextMap[" << context->id().value << "] = context;\n";
6963 out << "\n";
6964
6965 // Setup all of the shared objects.
6966 out << " InitReplay();\n";
6967 if (usesMidExecutionCapture())
6968 {
6969 out << " " << FmtSetupFunction(kNoPartId, kSharedContextId) << ";\n";
6970 }
6971
6972 // Setup the presentation (this) context first.
6973 out << " " << FmtSetupFunction(kNoPartId, context->id()) << ";\n";
6974 out << "\n";
6975
6976 // Setup each of the auxiliary contexts.
6977 egl::ShareGroup *shareGroup = context->getShareGroup();
6978 const egl::ContextSet &shareContextSet = shareGroup->getContexts();
6979 for (gl::Context *shareContext : shareContextSet)
6980 {
6981 // Skip the presentation context, since that context was created by the test
6982 // framework.
6983 if (shareContext->id() == context->id())
6984 {
6985 continue;
6986 }
6987
6988 // TODO(http://www.anglebug.com/5878): Support capture/replay of eglCreateContext()
6989 // so this block can be moved into SetupReplayContextXX() by injecting them into the
6990 // beginning of the setup call stream.
6991 out << " EGLContext context" << shareContext->id()
6992 << " = eglCreateContext(nullptr, nullptr, context, nullptr);\n";
6993 out << " gContextMap[" << shareContext->id().value << "] = context"
6994 << shareContext->id() << ";\n";
6995 // The SetupReplayContextXX() calls only exist if this is a mid-execution capture
6996 // and we can only call them if they exist, so only output the calls if this is a
6997 // MEC.
6998 if (usesMidExecutionCapture())
6999 {
7000 out << " " << FmtSetupFunction(kNoPartId, shareContext->id()) << ";\n";
7001 }
7002 }
7003
7004 // If there are other contexts that were initialized, we need to make the main context
7005 // current again.
7006 if (shareContextSet.size() > 1)
7007 {
7008 out << "\n";
7009 out << " eglMakeCurrent(EGL_NO_DISPLAY, EGL_NO_SURFACE, EGL_NO_SURFACE, "
7010 "context);\n";
7011 }
7012
7013 out << "}\n";
7014
7015 mReplayWriter.addPublicFunction(proto, std::stringstream(), out);
7016 }
7017 }
7018
7019 // Emit code to reset back to starting state
7020 if (frameIndex == frameCount)
7021 {
7022 std::stringstream protoStream;
7023 std::stringstream headerStream;
7024 std::stringstream bodyStream;
7025
7026 protoStream << "void " << FmtResetFunction();
7027 std::string proto = protoStream.str();
7028
7029 bodyStream << proto << "\n";
7030 bodyStream << "{\n";
7031
7032 // TODO(http://anglebug.com/5878): Look at moving this into the shared context file since
7033 // it's resetting shared objects.
7034 for (ResourceIDType resourceType : AllEnums<ResourceIDType>())
7035 {
7036 // Framebuffers must be done last so updated textures can be bound to them
7037 if (resourceType == ResourceIDType::Framebuffer)
7038 {
7039 continue;
7040 }
7041
7042 MaybeResetResources(resourceType, mReplayWriter, bodyStream, headerStream,
7043 &mResourceTracker, &mBinaryData);
7044 }
7045
7046 MaybeResetResources(ResourceIDType::Framebuffer, mReplayWriter, bodyStream, headerStream,
7047 &mResourceTracker, &mBinaryData);
7048
7049 // Reset opaque type objects that don't have IDs, so are not ResourceIDTypes.
7050 MaybeResetOpaqueTypeObjects(mReplayWriter, bodyStream, headerStream, &mResourceTracker,
7051 &mBinaryData);
7052
7053 bodyStream << "}\n";
7054
7055 mReplayWriter.addPublicFunction(proto, headerStream, bodyStream);
7056 }
7057
7058 if (!mFrameCalls.empty())
7059 {
7060 std::stringstream protoStream;
7061 protoStream << "void " << FmtReplayFunction(context->id(), frameIndex);
7062 std::string proto = protoStream.str();
7063
7064 std::stringstream headerStream;
7065 std::stringstream bodyStream;
7066
7067 WriteCppReplayFunctionWithParts(context->id(), ReplayFunc::Replay, mReplayWriter,
7068 frameIndex, &mBinaryData, mFrameCalls, headerStream,
7069 bodyStream);
7070
7071 mReplayWriter.addPrivateFunction(proto, headerStream, bodyStream);
7072 }
7073
7074 if (mSerializeStateEnabled)
7075 {
7076 std::string serializedContextString;
7077 if (SerializeContextToString(const_cast<gl::Context *>(context),
7078 &serializedContextString) == Result::Continue)
7079 {
7080 std::stringstream protoStream;
7081 protoStream << "const char *"
7082 << FmtGetSerializedContextStateFunction(context->id(), frameIndex);
7083 std::string proto = protoStream.str();
7084
7085 std::stringstream bodyStream;
7086 bodyStream << proto << "\n";
7087 bodyStream << "{\n";
7088 bodyStream << " return R\"(" << serializedContextString << ")\";\n";
7089 bodyStream << "}\n";
7090
7091 mReplayWriter.addPrivateFunction(proto, std::stringstream(), bodyStream);
7092 }
7093 }
7094
7095 {
7096 std::stringstream fnamePatternStream;
7097 fnamePatternStream << mOutDirectory << FmtCapturePrefix(context->id(), mCaptureLabel);
7098 std::string fnamePattern = fnamePatternStream.str();
7099
7100 mReplayWriter.setFilenamePattern(fnamePattern);
7101 }
7102
7103 if (mFrameIndex == mCaptureEndFrame)
7104 {
7105 mReplayWriter.saveFrame();
7106 }
7107 else
7108 {
7109 mReplayWriter.saveFrameIfFull();
7110 }
7111 }
7112
reset()7113 void FrameCaptureShared::reset()
7114 {
7115 mFrameCalls.clear();
7116 mClientVertexArrayMap.fill(-1);
7117
7118 // Do not reset replay-specific settings like the maximum read buffer size, client array sizes,
7119 // or the 'has seen' type map. We could refine this into per-frame and per-capture maximums if
7120 // necessary.
7121 }
7122
getShaderSource(gl::ShaderProgramID id) const7123 const std::string &FrameCaptureShared::getShaderSource(gl::ShaderProgramID id) const
7124 {
7125 const auto &foundSources = mCachedShaderSource.find(id);
7126 ASSERT(foundSources != mCachedShaderSource.end());
7127 return foundSources->second;
7128 }
7129
setShaderSource(gl::ShaderProgramID id,std::string source)7130 void FrameCaptureShared::setShaderSource(gl::ShaderProgramID id, std::string source)
7131 {
7132 mCachedShaderSource[id] = source;
7133 }
7134
getProgramSources(gl::ShaderProgramID id) const7135 const ProgramSources &FrameCaptureShared::getProgramSources(gl::ShaderProgramID id) const
7136 {
7137 const auto &foundSources = mCachedProgramSources.find(id);
7138 ASSERT(foundSources != mCachedProgramSources.end());
7139 return foundSources->second;
7140 }
7141
setProgramSources(gl::ShaderProgramID id,ProgramSources sources)7142 void FrameCaptureShared::setProgramSources(gl::ShaderProgramID id, ProgramSources sources)
7143 {
7144 mCachedProgramSources[id] = sources;
7145 }
7146
markResourceSetupCallsInactive(std::vector<CallCapture> * setupCalls,ResourceIDType type,GLuint id,gl::Range<size_t> range)7147 void FrameCaptureShared::markResourceSetupCallsInactive(std::vector<CallCapture> *setupCalls,
7148 ResourceIDType type,
7149 GLuint id,
7150 gl::Range<size_t> range)
7151 {
7152 if (!mTrimEnabled)
7153 {
7154 return;
7155 }
7156
7157 ASSERT(mResourceIDToSetupCalls[type].find(id) == mResourceIDToSetupCalls[type].end());
7158
7159 // Mark all of the calls that were used to initialize this resource as INACTIVE
7160 for (size_t index : range)
7161 {
7162 (*setupCalls)[index].isActive = false;
7163 }
7164
7165 mResourceIDToSetupCalls[type][id] = range;
7166 }
7167
CaptureMemory(const void * source,size_t size,ParamCapture * paramCapture)7168 void CaptureMemory(const void *source, size_t size, ParamCapture *paramCapture)
7169 {
7170 std::vector<uint8_t> data(size);
7171 memcpy(data.data(), source, size);
7172 paramCapture->data.emplace_back(std::move(data));
7173 }
7174
CaptureString(const GLchar * str,ParamCapture * paramCapture)7175 void CaptureString(const GLchar *str, ParamCapture *paramCapture)
7176 {
7177 // include the '\0' suffix
7178 CaptureMemory(str, strlen(str) + 1, paramCapture);
7179 }
7180
CaptureStringLimit(const GLchar * str,uint32_t limit,ParamCapture * paramCapture)7181 void CaptureStringLimit(const GLchar *str, uint32_t limit, ParamCapture *paramCapture)
7182 {
7183 // Write the incoming string up to limit, including null terminator
7184 size_t length = strlen(str) + 1;
7185
7186 if (length > limit)
7187 {
7188 // If too many characters, resize the string to fit in the limit
7189 std::string newStr = str;
7190 newStr.resize(limit - 1);
7191 CaptureString(newStr.c_str(), paramCapture);
7192 }
7193 else
7194 {
7195 CaptureMemory(str, length, paramCapture);
7196 }
7197 }
7198
CaptureVertexPointerGLES1(const gl::State & glState,gl::ClientVertexArrayType type,const void * pointer,ParamCapture * paramCapture)7199 void CaptureVertexPointerGLES1(const gl::State &glState,
7200 gl::ClientVertexArrayType type,
7201 const void *pointer,
7202 ParamCapture *paramCapture)
7203 {
7204 paramCapture->value.voidConstPointerVal = pointer;
7205 if (!glState.getTargetBuffer(gl::BufferBinding::Array))
7206 {
7207 paramCapture->arrayClientPointerIndex =
7208 gl::GLES1Renderer::VertexArrayIndex(type, glState.gles1());
7209 }
7210 }
7211
GetProgramForCapture(const gl::State & glState,gl::ShaderProgramID handle)7212 gl::Program *GetProgramForCapture(const gl::State &glState, gl::ShaderProgramID handle)
7213 {
7214 gl::Program *program = glState.getShaderProgramManagerForCapture().getProgram(handle);
7215 return program;
7216 }
7217
CaptureGetActiveUniformBlockivParameters(const gl::State & glState,gl::ShaderProgramID handle,gl::UniformBlockIndex uniformBlockIndex,GLenum pname,ParamCapture * paramCapture)7218 void CaptureGetActiveUniformBlockivParameters(const gl::State &glState,
7219 gl::ShaderProgramID handle,
7220 gl::UniformBlockIndex uniformBlockIndex,
7221 GLenum pname,
7222 ParamCapture *paramCapture)
7223 {
7224 int numParams = 1;
7225
7226 // From the OpenGL ES 3.0 spec:
7227 // If pname is UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES, then a list of the
7228 // active uniform indices for the uniform block identified by uniformBlockIndex is
7229 // returned. The number of elements that will be written to params is the value of
7230 // UNIFORM_BLOCK_ACTIVE_UNIFORMS for uniformBlockIndex
7231 if (pname == GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES)
7232 {
7233 gl::Program *program = GetProgramForCapture(glState, handle);
7234 if (program)
7235 {
7236 gl::QueryActiveUniformBlockiv(program, uniformBlockIndex,
7237 GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS, &numParams);
7238 }
7239 }
7240
7241 paramCapture->readBufferSizeBytes = sizeof(GLint) * numParams;
7242 }
7243
CaptureGetParameter(const gl::State & glState,GLenum pname,size_t typeSize,ParamCapture * paramCapture)7244 void CaptureGetParameter(const gl::State &glState,
7245 GLenum pname,
7246 size_t typeSize,
7247 ParamCapture *paramCapture)
7248 {
7249 // kMaxReportedCapabilities is the biggest array we'll need to hold data from glGet calls.
7250 // This value needs to be updated if any new extensions are introduced that would allow for
7251 // more compressed texture formats. The current value is taken from:
7252 // http://opengles.gpuinfo.org/displaycapability.php?name=GL_NUM_COMPRESSED_TEXTURE_FORMATS&esversion=2
7253 constexpr unsigned int kMaxReportedCapabilities = 69;
7254 paramCapture->readBufferSizeBytes = typeSize * kMaxReportedCapabilities;
7255 }
7256
CaptureGenHandlesImpl(GLsizei n,GLuint * handles,ParamCapture * paramCapture)7257 void CaptureGenHandlesImpl(GLsizei n, GLuint *handles, ParamCapture *paramCapture)
7258 {
7259 paramCapture->readBufferSizeBytes = sizeof(GLuint) * n;
7260 CaptureMemory(handles, paramCapture->readBufferSizeBytes, paramCapture);
7261 }
7262
CaptureShaderStrings(GLsizei count,const GLchar * const * strings,const GLint * length,ParamCapture * paramCapture)7263 void CaptureShaderStrings(GLsizei count,
7264 const GLchar *const *strings,
7265 const GLint *length,
7266 ParamCapture *paramCapture)
7267 {
7268 // Concat the array elements of the string into one data vector,
7269 // append the terminating zero and use this as the captured shader
7270 // string. The string count and the length array are adjusted
7271 // accordingly in the capture post-processing
7272
7273 std::vector<uint8_t> data;
7274 size_t offset = 0;
7275 for (GLsizei index = 0; index < count; ++index)
7276 {
7277 size_t len = ((length && length[index] >= 0) ? length[index] : strlen(strings[index]));
7278 data.resize(offset + len);
7279 std::copy(strings[index], strings[index] + len, data.begin() + offset);
7280 offset += len;
7281 }
7282 data.push_back(0);
7283 paramCapture->data.emplace_back(std::move(data));
7284 }
7285
7286 template <>
WriteParamValueReplay(std::ostream & os,const CallCapture & call,GLboolean value)7287 void WriteParamValueReplay<ParamType::TGLboolean>(std::ostream &os,
7288 const CallCapture &call,
7289 GLboolean value)
7290 {
7291 switch (value)
7292 {
7293 case GL_TRUE:
7294 os << "GL_TRUE";
7295 break;
7296 case GL_FALSE:
7297 os << "GL_FALSE";
7298 break;
7299 default:
7300 os << "0x" << std::hex << std::uppercase << GLint(value);
7301 }
7302 }
7303
7304 template <>
WriteParamValueReplay(std::ostream & os,const CallCapture & call,GLboolean * value)7305 void WriteParamValueReplay<ParamType::TGLbooleanPointer>(std::ostream &os,
7306 const CallCapture &call,
7307 GLboolean *value)
7308 {
7309 if (value == 0)
7310 {
7311 os << "nullptr";
7312 }
7313 else
7314 {
7315 os << "reinterpret_cast<GLboolean *>("
7316 << static_cast<int>(reinterpret_cast<uintptr_t>(value)) << ")";
7317 }
7318 }
7319
7320 template <>
WriteParamValueReplay(std::ostream & os,const CallCapture & call,const void * value)7321 void WriteParamValueReplay<ParamType::TvoidConstPointer>(std::ostream &os,
7322 const CallCapture &call,
7323 const void *value)
7324 {
7325 if (value == 0)
7326 {
7327 os << "nullptr";
7328 }
7329 else
7330 {
7331 os << "reinterpret_cast<const void *>("
7332 << static_cast<int>(reinterpret_cast<uintptr_t>(value)) << ")";
7333 }
7334 }
7335
7336 template <>
WriteParamValueReplay(std::ostream & os,const CallCapture & call,void * value)7337 void WriteParamValueReplay<ParamType::TvoidPointer>(std::ostream &os,
7338 const CallCapture &call,
7339 void *value)
7340 {
7341 if (value == 0)
7342 {
7343 os << "nullptr";
7344 }
7345 else
7346 {
7347 os << "reinterpret_cast<void *>(" << static_cast<int>(reinterpret_cast<uintptr_t>(value))
7348 << ")";
7349 }
7350 }
7351
7352 template <>
WriteParamValueReplay(std::ostream & os,const CallCapture & call,const GLfloat * value)7353 void WriteParamValueReplay<ParamType::TGLfloatConstPointer>(std::ostream &os,
7354 const CallCapture &call,
7355 const GLfloat *value)
7356 {
7357 if (value == 0)
7358 {
7359 os << "nullptr";
7360 }
7361 else
7362 {
7363 os << "reinterpret_cast<const GLfloat *>("
7364 << static_cast<int>(reinterpret_cast<uintptr_t>(value)) << ")";
7365 }
7366 }
7367
7368 template <>
WriteParamValueReplay(std::ostream & os,const CallCapture & call,const GLint * value)7369 void WriteParamValueReplay<ParamType::TGLintConstPointer>(std::ostream &os,
7370 const CallCapture &call,
7371 const GLint *value)
7372 {
7373 if (value == 0)
7374 {
7375 os << "nullptr";
7376 }
7377 else
7378 {
7379 os << "reinterpret_cast<const GLint *>("
7380 << static_cast<int>(reinterpret_cast<intptr_t>(value)) << ")";
7381 }
7382 }
7383
7384 template <>
WriteParamValueReplay(std::ostream & os,const CallCapture & call,GLsizei * value)7385 void WriteParamValueReplay<ParamType::TGLsizeiPointer>(std::ostream &os,
7386 const CallCapture &call,
7387 GLsizei *value)
7388 {
7389 if (value == 0)
7390 {
7391 os << "nullptr";
7392 }
7393 else
7394 {
7395 os << "reinterpret_cast<GLsizei *>(" << static_cast<int>(reinterpret_cast<intptr_t>(value))
7396 << ")";
7397 }
7398 }
7399
7400 template <>
WriteParamValueReplay(std::ostream & os,const CallCapture & call,const GLuint * value)7401 void WriteParamValueReplay<ParamType::TGLuintConstPointer>(std::ostream &os,
7402 const CallCapture &call,
7403 const GLuint *value)
7404 {
7405 if (value == 0)
7406 {
7407 os << "nullptr";
7408 }
7409 else
7410 {
7411 os << "reinterpret_cast<const GLuint *>("
7412 << static_cast<int>(reinterpret_cast<uintptr_t>(value)) << ")";
7413 }
7414 }
7415
7416 template <>
WriteParamValueReplay(std::ostream & os,const CallCapture & call,GLDEBUGPROCKHR value)7417 void WriteParamValueReplay<ParamType::TGLDEBUGPROCKHR>(std::ostream &os,
7418 const CallCapture &call,
7419 GLDEBUGPROCKHR value)
7420 {}
7421
7422 template <>
WriteParamValueReplay(std::ostream & os,const CallCapture & call,GLDEBUGPROC value)7423 void WriteParamValueReplay<ParamType::TGLDEBUGPROC>(std::ostream &os,
7424 const CallCapture &call,
7425 GLDEBUGPROC value)
7426 {}
7427
7428 template <>
WriteParamValueReplay(std::ostream & os,const CallCapture & call,gl::BufferID value)7429 void WriteParamValueReplay<ParamType::TBufferID>(std::ostream &os,
7430 const CallCapture &call,
7431 gl::BufferID value)
7432 {
7433 os << "gBufferMap[" << value.value << "]";
7434 }
7435
7436 template <>
WriteParamValueReplay(std::ostream & os,const CallCapture & call,gl::FenceNVID value)7437 void WriteParamValueReplay<ParamType::TFenceNVID>(std::ostream &os,
7438 const CallCapture &call,
7439 gl::FenceNVID value)
7440 {
7441 os << "gFenceNVMap[" << value.value << "]";
7442 }
7443
7444 template <>
WriteParamValueReplay(std::ostream & os,const CallCapture & call,gl::FramebufferID value)7445 void WriteParamValueReplay<ParamType::TFramebufferID>(std::ostream &os,
7446 const CallCapture &call,
7447 gl::FramebufferID value)
7448 {
7449 os << "gFramebufferMap[" << value.value << "]";
7450 }
7451
7452 template <>
WriteParamValueReplay(std::ostream & os,const CallCapture & call,gl::MemoryObjectID value)7453 void WriteParamValueReplay<ParamType::TMemoryObjectID>(std::ostream &os,
7454 const CallCapture &call,
7455 gl::MemoryObjectID value)
7456 {
7457 os << "gMemoryObjectMap[" << value.value << "]";
7458 }
7459
7460 template <>
WriteParamValueReplay(std::ostream & os,const CallCapture & call,gl::ProgramPipelineID value)7461 void WriteParamValueReplay<ParamType::TProgramPipelineID>(std::ostream &os,
7462 const CallCapture &call,
7463 gl::ProgramPipelineID value)
7464 {
7465 os << "gProgramPipelineMap[" << value.value << "]";
7466 }
7467
7468 template <>
WriteParamValueReplay(std::ostream & os,const CallCapture & call,gl::QueryID value)7469 void WriteParamValueReplay<ParamType::TQueryID>(std::ostream &os,
7470 const CallCapture &call,
7471 gl::QueryID value)
7472 {
7473 os << "gQueryMap[" << value.value << "]";
7474 }
7475
7476 template <>
WriteParamValueReplay(std::ostream & os,const CallCapture & call,gl::RenderbufferID value)7477 void WriteParamValueReplay<ParamType::TRenderbufferID>(std::ostream &os,
7478 const CallCapture &call,
7479 gl::RenderbufferID value)
7480 {
7481 os << "gRenderbufferMap[" << value.value << "]";
7482 }
7483
7484 template <>
WriteParamValueReplay(std::ostream & os,const CallCapture & call,gl::SamplerID value)7485 void WriteParamValueReplay<ParamType::TSamplerID>(std::ostream &os,
7486 const CallCapture &call,
7487 gl::SamplerID value)
7488 {
7489 os << "gSamplerMap[" << value.value << "]";
7490 }
7491
7492 template <>
WriteParamValueReplay(std::ostream & os,const CallCapture & call,gl::SemaphoreID value)7493 void WriteParamValueReplay<ParamType::TSemaphoreID>(std::ostream &os,
7494 const CallCapture &call,
7495 gl::SemaphoreID value)
7496 {
7497 os << "gSemaphoreMap[" << value.value << "]";
7498 }
7499
7500 template <>
WriteParamValueReplay(std::ostream & os,const CallCapture & call,gl::ShaderProgramID value)7501 void WriteParamValueReplay<ParamType::TShaderProgramID>(std::ostream &os,
7502 const CallCapture &call,
7503 gl::ShaderProgramID value)
7504 {
7505 os << "gShaderProgramMap[" << value.value << "]";
7506 }
7507
7508 template <>
WriteParamValueReplay(std::ostream & os,const CallCapture & call,GLsync value)7509 void WriteParamValueReplay<ParamType::TGLsync>(std::ostream &os,
7510 const CallCapture &call,
7511 GLsync value)
7512 {
7513 os << "gSyncMap[" << SyncIndexValue(value) << "]";
7514 }
7515
7516 template <>
WriteParamValueReplay(std::ostream & os,const CallCapture & call,gl::TextureID value)7517 void WriteParamValueReplay<ParamType::TTextureID>(std::ostream &os,
7518 const CallCapture &call,
7519 gl::TextureID value)
7520 {
7521 os << "gTextureMap[" << value.value << "]";
7522 }
7523
7524 template <>
WriteParamValueReplay(std::ostream & os,const CallCapture & call,gl::TransformFeedbackID value)7525 void WriteParamValueReplay<ParamType::TTransformFeedbackID>(std::ostream &os,
7526 const CallCapture &call,
7527 gl::TransformFeedbackID value)
7528 {
7529 os << "gTransformFeedbackMap[" << value.value << "]";
7530 }
7531
7532 template <>
WriteParamValueReplay(std::ostream & os,const CallCapture & call,gl::VertexArrayID value)7533 void WriteParamValueReplay<ParamType::TVertexArrayID>(std::ostream &os,
7534 const CallCapture &call,
7535 gl::VertexArrayID value)
7536 {
7537 os << "gVertexArrayMap[" << value.value << "]";
7538 }
7539
7540 template <>
WriteParamValueReplay(std::ostream & os,const CallCapture & call,gl::UniformLocation value)7541 void WriteParamValueReplay<ParamType::TUniformLocation>(std::ostream &os,
7542 const CallCapture &call,
7543 gl::UniformLocation value)
7544 {
7545 if (value.value == -1)
7546 {
7547 os << "-1";
7548 return;
7549 }
7550
7551 os << "gUniformLocations[";
7552
7553 // Find the program from the call parameters.
7554 std::vector<gl::ShaderProgramID> programIDs;
7555 if (FindShaderProgramIDsInCall(call, programIDs))
7556 {
7557 ASSERT(programIDs.size() == 1);
7558 os << "gShaderProgramMap[" << programIDs[0].value << "]";
7559 }
7560 else
7561 {
7562 os << "gCurrentProgram";
7563 }
7564
7565 os << "][" << value.value << "]";
7566 }
7567
7568 template <>
WriteParamValueReplay(std::ostream & os,const CallCapture & call,gl::UniformBlockIndex value)7569 void WriteParamValueReplay<ParamType::TUniformBlockIndex>(std::ostream &os,
7570 const CallCapture &call,
7571 gl::UniformBlockIndex value)
7572 {
7573 // Find the program from the call parameters.
7574 std::vector<gl::ShaderProgramID> programIDs;
7575 bool foundProgram = FindShaderProgramIDsInCall(call, programIDs);
7576 ASSERT(foundProgram && programIDs.size() == 1);
7577
7578 os << "gUniformBlockIndexes[gShaderProgramMap[" << programIDs[0].value << "]][" << value.value
7579 << "]";
7580 }
7581
7582 template <>
WriteParamValueReplay(std::ostream & os,const CallCapture & call,GLeglImageOES value)7583 void WriteParamValueReplay<ParamType::TGLeglImageOES>(std::ostream &os,
7584 const CallCapture &call,
7585 GLeglImageOES value)
7586 {
7587 uint64_t pointerValue = reinterpret_cast<uint64_t>(value);
7588 os << "reinterpret_cast<EGLImageKHR>(" << pointerValue << "ul)";
7589 }
7590
7591 template <>
WriteParamValueReplay(std::ostream & os,const CallCapture & call,GLubyte value)7592 void WriteParamValueReplay<ParamType::TGLubyte>(std::ostream &os,
7593 const CallCapture &call,
7594 GLubyte value)
7595 {
7596 const int v = value;
7597 os << v;
7598 }
7599
7600 template <>
WriteParamValueReplay(std::ostream & os,const CallCapture & call,EGLContext value)7601 void WriteParamValueReplay<ParamType::TEGLContext>(std::ostream &os,
7602 const CallCapture &call,
7603 EGLContext value)
7604 {
7605 os << "gContextMap[" << reinterpret_cast<size_t>(value) << "]";
7606 }
7607
7608 template <>
WriteParamValueReplay(std::ostream & os,const CallCapture & call,EGLDisplay value)7609 void WriteParamValueReplay<ParamType::TEGLDisplay>(std::ostream &os,
7610 const CallCapture &call,
7611 EGLDisplay value)
7612 {
7613 if (value == EGL_NO_DISPLAY)
7614 {
7615 os << "EGL_NO_DISPLAY";
7616 return;
7617 }
7618
7619 // We don't support capturing real EGL calls.
7620 UNREACHABLE();
7621 }
7622
7623 template <>
WriteParamValueReplay(std::ostream & os,const CallCapture & call,EGLSurface value)7624 void WriteParamValueReplay<ParamType::TEGLSurface>(std::ostream &os,
7625 const CallCapture &call,
7626 EGLSurface value)
7627 {
7628 if (value == EGL_NO_SURFACE)
7629 {
7630 os << "EGL_NO_SURFACE";
7631 return;
7632 }
7633
7634 // We don't support capturing real EGL calls.
7635 UNREACHABLE();
7636 }
7637
7638 template <>
WriteParamValueReplay(std::ostream & os,const CallCapture & call,EGLDEBUGPROCKHR value)7639 void WriteParamValueReplay<ParamType::TEGLDEBUGPROCKHR>(std::ostream &os,
7640 const CallCapture &call,
7641 EGLDEBUGPROCKHR value)
7642 {
7643 // The value isn't actually useful, but this fixes MSVC compile errors:
7644 // error: implicit conversion between pointer-to-function and pointer-to-object is a Microsoft
7645 // extension [-Werror,-Wmicrosoft-cast]
7646 os << reinterpret_cast<void *>(value);
7647 }
7648
7649 template <>
WriteParamValueReplay(std::ostream & os,const CallCapture & call,EGLGetBlobFuncANDROID value)7650 void WriteParamValueReplay<ParamType::TEGLGetBlobFuncANDROID>(std::ostream &os,
7651 const CallCapture &call,
7652 EGLGetBlobFuncANDROID value)
7653 {
7654 // The value isn't actually useful, but this fixes MSVC compile errors:
7655 // error: implicit conversion between pointer-to-function and pointer-to-object is a Microsoft
7656 // extension [-Werror,-Wmicrosoft-cast]
7657 os << reinterpret_cast<void *>(value);
7658 }
7659
7660 template <>
WriteParamValueReplay(std::ostream & os,const CallCapture & call,EGLSetBlobFuncANDROID value)7661 void WriteParamValueReplay<ParamType::TEGLSetBlobFuncANDROID>(std::ostream &os,
7662 const CallCapture &call,
7663 EGLSetBlobFuncANDROID value)
7664 {
7665 // The value isn't actually useful, but this fixes MSVC compile errors:
7666 // error: implicit conversion between pointer-to-function and pointer-to-object is a Microsoft
7667 // extension [-Werror,-Wmicrosoft-cast]
7668 os << reinterpret_cast<void *>(value);
7669 }
7670
7671 // ReplayWriter implementation.
ReplayWriter()7672 ReplayWriter::ReplayWriter()
7673 : mSourceFileSizeThreshold(kDefaultSourceFileSizeThreshold), mFrameIndex(1)
7674 {}
7675
~ReplayWriter()7676 ReplayWriter::~ReplayWriter()
7677 {
7678 ASSERT(mPrivateFunctionPrototypes.empty());
7679 ASSERT(mPublicFunctionPrototypes.empty());
7680 ASSERT(mPrivateFunctions.empty());
7681 ASSERT(mPublicFunctions.empty());
7682 ASSERT(mGlobalVariableDeclarations.empty());
7683 ASSERT(mReplayHeaders.empty());
7684 }
7685
setSourceFileSizeThreshold(size_t sourceFileSizeThreshold)7686 void ReplayWriter::setSourceFileSizeThreshold(size_t sourceFileSizeThreshold)
7687 {
7688 mSourceFileSizeThreshold = sourceFileSizeThreshold;
7689 }
7690
setFilenamePattern(const std::string & pattern)7691 void ReplayWriter::setFilenamePattern(const std::string &pattern)
7692 {
7693 if (mFilenamePattern != pattern)
7694 {
7695 mFilenamePattern = pattern;
7696
7697 // Reset the frame counter if the filename pattern changes.
7698 mFrameIndex = 1;
7699 }
7700 }
7701
setCaptureLabel(const std::string & label)7702 void ReplayWriter::setCaptureLabel(const std::string &label)
7703 {
7704 mCaptureLabel = label;
7705 }
7706
setSourcePrologue(const std::string & prologue)7707 void ReplayWriter::setSourcePrologue(const std::string &prologue)
7708 {
7709 mSourcePrologue = prologue;
7710 }
7711
setHeaderPrologue(const std::string & prologue)7712 void ReplayWriter::setHeaderPrologue(const std::string &prologue)
7713 {
7714 mHeaderPrologue = prologue;
7715 }
7716
addPublicFunction(const std::string & functionProto,const std::stringstream & headerStream,const std::stringstream & bodyStream)7717 void ReplayWriter::addPublicFunction(const std::string &functionProto,
7718 const std::stringstream &headerStream,
7719 const std::stringstream &bodyStream)
7720 {
7721 mPublicFunctionPrototypes.push_back(functionProto);
7722
7723 std::string header = headerStream.str();
7724 std::string body = bodyStream.str();
7725
7726 if (!header.empty())
7727 {
7728 mReplayHeaders.emplace_back(header);
7729 }
7730
7731 if (!body.empty())
7732 {
7733 mPublicFunctions.emplace_back(body);
7734 }
7735 }
7736
addPrivateFunction(const std::string & functionProto,const std::stringstream & headerStream,const std::stringstream & bodyStream)7737 void ReplayWriter::addPrivateFunction(const std::string &functionProto,
7738 const std::stringstream &headerStream,
7739 const std::stringstream &bodyStream)
7740 {
7741 mPrivateFunctionPrototypes.push_back(functionProto);
7742
7743 std::string header = headerStream.str();
7744 std::string body = bodyStream.str();
7745
7746 if (!header.empty())
7747 {
7748 mReplayHeaders.emplace_back(header);
7749 }
7750
7751 if (!body.empty())
7752 {
7753 mPrivateFunctions.emplace_back(body);
7754 }
7755 }
7756
getInlineVariableName(EntryPoint entryPoint,const std::string & paramName)7757 std::string ReplayWriter::getInlineVariableName(EntryPoint entryPoint, const std::string ¶mName)
7758 {
7759 int counter = mDataTracker.getCounters().getAndIncrement(entryPoint, paramName);
7760 return GetVarName(entryPoint, paramName, counter);
7761 }
7762
getInlineStringSetVariableName(EntryPoint entryPoint,const std::string & paramName,const std::vector<std::string> & strings,bool * isNewEntryOut)7763 std::string ReplayWriter::getInlineStringSetVariableName(EntryPoint entryPoint,
7764 const std::string ¶mName,
7765 const std::vector<std::string> &strings,
7766 bool *isNewEntryOut)
7767 {
7768 int counter = mDataTracker.getStringCounters().getStringCounter(strings);
7769 *isNewEntryOut = (counter == kStringsNotFound);
7770 if (*isNewEntryOut)
7771 {
7772 // This is a unique set of strings, so set up their declaration and update the counter
7773 counter = mDataTracker.getCounters().getAndIncrement(entryPoint, paramName);
7774 mDataTracker.getStringCounters().setStringCounter(strings, counter);
7775
7776 std::string varName = GetVarName(entryPoint, paramName, counter);
7777
7778 std::stringstream declStream;
7779 declStream << "const char *const " << varName << "[]";
7780 std::string decl = declStream.str();
7781
7782 mGlobalVariableDeclarations.push_back(decl);
7783
7784 return varName;
7785 }
7786 else
7787 {
7788 return GetVarName(entryPoint, paramName, counter);
7789 }
7790 }
7791
getStoredReplaySourceSize() const7792 size_t ReplayWriter::getStoredReplaySourceSize() const
7793 {
7794 size_t sum = 0;
7795 for (const std::string &header : mReplayHeaders)
7796 {
7797 sum += header.size();
7798 }
7799 for (const std::string &publicFunc : mPublicFunctions)
7800 {
7801 sum += publicFunc.size();
7802 }
7803 for (const std::string &privateFunc : mPrivateFunctions)
7804 {
7805 sum += privateFunc.size();
7806 }
7807 return sum;
7808 }
7809
7810 // static
GetVarName(EntryPoint entryPoint,const std::string & paramName,int counter)7811 std::string ReplayWriter::GetVarName(EntryPoint entryPoint,
7812 const std::string ¶mName,
7813 int counter)
7814 {
7815 std::stringstream strstr;
7816 strstr << GetEntryPointName(entryPoint) << "_" << paramName << "_" << counter;
7817 return strstr.str();
7818 }
7819
saveFrame()7820 void ReplayWriter::saveFrame()
7821 {
7822 if (mReplayHeaders.empty() && mPublicFunctions.empty() && mPrivateFunctions.empty())
7823 {
7824 return;
7825 }
7826
7827 std::stringstream strstr;
7828 strstr << mFilenamePattern << "_" << std::setfill('0') << std::setw(3) << mFrameIndex++
7829 << ".cpp";
7830
7831 std::string frameFilePath = strstr.str();
7832
7833 writeReplaySource(frameFilePath);
7834 }
7835
saveFrameIfFull()7836 void ReplayWriter::saveFrameIfFull()
7837 {
7838 if (getStoredReplaySourceSize() < mSourceFileSizeThreshold)
7839 {
7840 INFO() << "Merging captured frame: " << getStoredReplaySourceSize()
7841 << " less than threshold of " << mSourceFileSizeThreshold << " bytes";
7842 return;
7843 }
7844
7845 saveFrame();
7846 }
7847
saveHeader()7848 void ReplayWriter::saveHeader()
7849 {
7850 std::stringstream headerPathStream;
7851 headerPathStream << mFilenamePattern << ".h";
7852 std::string headerPath = headerPathStream.str();
7853
7854 SaveFileHelper saveH(headerPath);
7855
7856 saveH << mHeaderPrologue << "\n";
7857
7858 saveH << "// Public functions are declared in trace_fixture.h.\n";
7859 saveH << "\n";
7860 saveH << "// Private Functions\n";
7861 saveH << "\n";
7862
7863 for (const std::string &proto : mPrivateFunctionPrototypes)
7864 {
7865 saveH << proto << ";\n";
7866 }
7867
7868 saveH << "\n";
7869 saveH << "// Global variables\n";
7870 saveH << "\n";
7871
7872 for (const std::string &globalVar : mGlobalVariableDeclarations)
7873 {
7874 saveH << "extern " << globalVar << ";\n";
7875 }
7876
7877 mPublicFunctionPrototypes.clear();
7878 mPrivateFunctionPrototypes.clear();
7879 mGlobalVariableDeclarations.clear();
7880
7881 addWrittenFile(headerPath);
7882 }
7883
saveIndexFilesAndHeader()7884 void ReplayWriter::saveIndexFilesAndHeader()
7885 {
7886 std::stringstream sourcePathStream;
7887 sourcePathStream << mFilenamePattern << ".cpp";
7888 std::string sourcePath = sourcePathStream.str();
7889
7890 writeReplaySource(sourcePath);
7891 saveHeader();
7892 }
7893
saveSetupFile()7894 void ReplayWriter::saveSetupFile()
7895 {
7896 std::stringstream strstr;
7897 strstr << mFilenamePattern << ".cpp";
7898
7899 std::string frameFilePath = strstr.str();
7900
7901 writeReplaySource(frameFilePath);
7902 }
7903
writeReplaySource(const std::string & filename)7904 void ReplayWriter::writeReplaySource(const std::string &filename)
7905 {
7906 SaveFileHelper saveCpp(filename);
7907
7908 saveCpp << mSourcePrologue << "\n";
7909
7910 for (const std::string &header : mReplayHeaders)
7911 {
7912 saveCpp << header << "\n";
7913 }
7914
7915 saveCpp << "// Private Functions\n";
7916 saveCpp << "\n";
7917
7918 for (const std::string &func : mPrivateFunctions)
7919 {
7920 saveCpp << func << "\n";
7921 }
7922
7923 saveCpp << "// Public Functions\n";
7924 saveCpp << "\n";
7925 saveCpp << "extern \"C\"\n";
7926 saveCpp << "{\n";
7927
7928 for (const std::string &func : mPublicFunctions)
7929 {
7930 saveCpp << func << "\n";
7931 }
7932
7933 saveCpp << "} // extern \"C\"\n";
7934
7935 mReplayHeaders.clear();
7936 mPrivateFunctions.clear();
7937 mPublicFunctions.clear();
7938
7939 addWrittenFile(filename);
7940 }
7941
addWrittenFile(const std::string & filename)7942 void ReplayWriter::addWrittenFile(const std::string &filename)
7943 {
7944 std::string writtenFile = GetBaseName(filename);
7945 ASSERT(std::find(mWrittenFiles.begin(), mWrittenFiles.end(), writtenFile) ==
7946 mWrittenFiles.end());
7947 mWrittenFiles.push_back(writtenFile);
7948 }
7949
getAndResetWrittenFiles()7950 std::vector<std::string> ReplayWriter::getAndResetWrittenFiles()
7951 {
7952 std::vector<std::string> results = std::move(mWrittenFiles);
7953 std::sort(results.begin(), results.end());
7954 ASSERT(mWrittenFiles.empty());
7955 return results;
7956 }
7957 } // namespace angle
7958