• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
2 // Copyright 2025 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 // FrameCaptureCommon.cpp:
7 //   ANGLE Frame capture implementation for both GL and CL.
8 //
9 
10 #include "libANGLE/capture/FrameCapture.h"
11 
12 #define USE_SYSTEM_ZLIB
13 #include "compression_utils_portable.h"
14 
15 namespace angle
16 {
17 
GetBinaryDataFilePath(bool compression,const std::string & captureLabel)18 std::string GetBinaryDataFilePath(bool compression, const std::string &captureLabel)
19 {
20     std::stringstream fnameStream;
21     fnameStream << FmtCapturePrefix(kNoContextId, captureLabel) << ".angledata";
22     if (compression)
23     {
24         fnameStream << ".gz";
25     }
26     return fnameStream.str();
27 }
28 
SaveBinaryData(bool compression,const std::string & outDir,gl::ContextID contextId,const std::string & captureLabel,const std::vector<uint8_t> & binaryData)29 void SaveBinaryData(bool compression,
30                     const std::string &outDir,
31                     gl::ContextID contextId,
32                     const std::string &captureLabel,
33                     const std::vector<uint8_t> &binaryData)
34 {
35     std::string binaryDataFileName = GetBinaryDataFilePath(compression, captureLabel);
36     std::string dataFilepath       = outDir + binaryDataFileName;
37 
38     SaveFileHelper saveData(dataFilepath);
39 
40     if (compression)
41     {
42         // Save compressed data.
43         uLong uncompressedSize       = static_cast<uLong>(binaryData.size());
44         uLong expectedCompressedSize = zlib_internal::GzipExpectedCompressedSize(uncompressedSize);
45 
46         std::vector<uint8_t> compressedData(expectedCompressedSize, 0);
47 
48         uLong compressedSize = expectedCompressedSize;
49         int zResult = zlib_internal::GzipCompressHelper(compressedData.data(), &compressedSize,
50                                                         binaryData.data(), uncompressedSize,
51                                                         nullptr, nullptr);
52 
53         if (zResult != Z_OK)
54         {
55             FATAL() << "Error compressing binary data: " << zResult;
56         }
57 
58         saveData.write(compressedData.data(), compressedSize);
59     }
60     else
61     {
62         saveData.write(binaryData.data(), binaryData.size());
63     }
64 }
65 
66 template <>
WriteInlineData(const std::vector<uint8_t> & vec,std::ostream & out)67 void WriteInlineData<GLchar>(const std::vector<uint8_t> &vec, std::ostream &out)
68 {
69     const GLchar *data = reinterpret_cast<const GLchar *>(vec.data());
70     size_t count       = vec.size() / sizeof(GLchar);
71 
72     if (data == nullptr || data[0] == '\0')
73     {
74         return;
75     }
76 
77     out << "\"";
78 
79     for (size_t dataIndex = 0; dataIndex < count; ++dataIndex)
80     {
81         if (data[dataIndex] == '\0')
82             break;
83 
84         out << static_cast<GLchar>(data[dataIndex]);
85     }
86 
87     out << "\"";
88 }
89 
WriteBinaryParamReplay(ReplayWriter & replayWriter,std::ostream & out,std::ostream & header,const CallCapture & call,const ParamCapture & param,std::vector<uint8_t> * binaryData)90 void WriteBinaryParamReplay(ReplayWriter &replayWriter,
91                             std::ostream &out,
92                             std::ostream &header,
93                             const CallCapture &call,
94                             const ParamCapture &param,
95                             std::vector<uint8_t> *binaryData)
96 {
97     std::string varName = replayWriter.getInlineVariableName(call.entryPoint, param.name);
98 
99     ASSERT(param.data.size() == 1);
100     const std::vector<uint8_t> &data = param.data[0];
101 
102     // Only inline strings (shaders) to simplify the C code.
103     ParamType overrideType = param.type;
104     if (param.type == ParamType::TvoidConstPointer)
105     {
106         overrideType = ParamType::TGLubyteConstPointer;
107     }
108     if (overrideType == ParamType::TGLcharPointer || overrideType == ParamType::TcharConstPointer)
109     {
110         // Inline if data is of type string
111         std::string paramTypeString = ParamTypeToString(param.type);
112         header << paramTypeString.substr(0, paramTypeString.length() - 1) << varName << "[] = { ";
113         WriteInlineData<GLchar>(data, header);
114         header << " };\n";
115         out << varName;
116     }
117     else
118     {
119         // Store in binary file if data are not of type string
120         // Round up to 16-byte boundary for cross ABI safety
121         size_t offset = rx::roundUpPow2(binaryData->size(), kBinaryAlignment);
122         binaryData->resize(offset + data.size());
123         memcpy(binaryData->data() + offset, data.data(), data.size());
124         out << "(" << ParamTypeToString(overrideType) << ")&gBinaryData[" << offset << "]";
125     }
126 }
127 
WriteStringPointerParamReplay(ReplayWriter & replayWriter,std::ostream & out,std::ostream & header,const CallCapture & call,const ParamCapture & param)128 void WriteStringPointerParamReplay(ReplayWriter &replayWriter,
129                                    std::ostream &out,
130                                    std::ostream &header,
131                                    const CallCapture &call,
132                                    const ParamCapture &param)
133 {
134     // Concatenate the strings to ensure we get an accurate counter
135     std::vector<std::string> strings;
136     for (const std::vector<uint8_t> &data : param.data)
137     {
138         // null terminate C style string
139         ASSERT(data.size() > 0 && data.back() == '\0');
140         strings.emplace_back(data.begin(), data.end() - 1);
141     }
142 
143     bool isNewEntry     = false;
144     std::string varName = replayWriter.getInlineStringSetVariableName(call.entryPoint, param.name,
145                                                                       strings, &isNewEntry);
146 
147     if (isNewEntry)
148     {
149         header << "const char *" << (replayWriter.captureAPI == CaptureAPI::CL ? " " : "const ")
150                << varName << "[] = { \n";
151 
152         for (const std::string &str : strings)
153         {
154             // Break up long strings for MSVC
155             size_t copyLength = 0;
156             std::string separator;
157             for (size_t i = 0; i < str.length(); i += kStringLengthLimit)
158             {
159                 if ((str.length() - i) <= kStringLengthLimit)
160                 {
161                     copyLength = str.length() - i;
162                     separator  = ",";
163                 }
164                 else
165                 {
166                     copyLength = kStringLengthLimit;
167                     separator  = "";
168                 }
169 
170                 header << FmtMultiLineString(str.substr(i, copyLength)) << separator << "\n";
171             }
172         }
173 
174         header << "};\n";
175     }
176 
177     out << varName;
178 }
179 
WriteComment(std::ostream & out,const CallCapture & call)180 void WriteComment(std::ostream &out, const CallCapture &call)
181 {
182     // Read the string parameter
183     const ParamCapture &stringParam =
184         call.params.getParam("comment", ParamType::TGLcharConstPointer, 0);
185     const std::vector<uint8_t> &data = stringParam.data[0];
186     ASSERT(data.size() > 0 && data.back() == '\0');
187     std::string str(data.begin(), data.end() - 1);
188 
189     // Write the string prefixed with single line comment
190     out << "// " << str;
191 }
192 
EscapeString(const std::string & string)193 std::string EscapeString(const std::string &string)
194 {
195     std::stringstream strstr;
196 
197     for (char c : string)
198     {
199         if (c == '\"' || c == '\\')
200         {
201             strstr << "\\";
202         }
203         strstr << c;
204     }
205 
206     return strstr.str();
207 }
208 
operator <<(std::ostream & os,gl::ContextID contextId)209 std::ostream &operator<<(std::ostream &os, gl::ContextID contextId)
210 {
211     os << static_cast<int>(contextId.value);
212     return os;
213 }
214 
operator <<(std::ostream & os,const FmtCapturePrefix & fmt)215 std::ostream &operator<<(std::ostream &os, const FmtCapturePrefix &fmt)
216 {
217     if (fmt.captureLabel.empty())
218     {
219         os << "angle_capture";
220     }
221     else
222     {
223         os << fmt.captureLabel;
224     }
225 
226     if (fmt.contextId == kSharedContextId)
227     {
228         os << "_shared";
229     }
230 
231     return os;
232 }
233 
operator <<(std::ostream & os,FuncUsage usage)234 std::ostream &operator<<(std::ostream &os, FuncUsage usage)
235 {
236     os << "(";
237     if (usage != FuncUsage::Call)
238     {
239         os << "void";
240     }
241     os << ")";
242     return os;
243 }
244 
operator <<(std::ostream & os,const FmtReplayFunction & fmt)245 std::ostream &operator<<(std::ostream &os, const FmtReplayFunction &fmt)
246 {
247     os << "Replay";
248 
249     if (fmt.contextId == kSharedContextId)
250     {
251         os << "Shared";
252     }
253 
254     os << "Frame" << fmt.frameIndex;
255 
256     if (fmt.partId != kNoPartId)
257     {
258         os << "Part" << fmt.partId;
259     }
260     os << fmt.usage;
261     return os;
262 }
263 
operator <<(std::ostream & os,const FmtSetupFunction & fmt)264 std::ostream &operator<<(std::ostream &os, const FmtSetupFunction &fmt)
265 {
266     os << "SetupReplay";
267 
268     if (fmt.contextId != kNoContextId)
269     {
270         os << "Context";
271     }
272 
273     if (fmt.contextId == kSharedContextId)
274     {
275         os << "Shared";
276     }
277     else
278     {
279         os << fmt.contextId;
280     }
281 
282     if (fmt.partId != kNoPartId)
283     {
284         os << "Part" << fmt.partId;
285     }
286     os << fmt.usage;
287     return os;
288 }
289 
operator <<(std::ostream & os,const FmtSetupFirstFrameFunction & fmt)290 std::ostream &operator<<(std::ostream &os, const FmtSetupFirstFrameFunction &fmt)
291 {
292     os << "SetupFirstFrame()";
293     return os;
294 }
295 
operator <<(std::ostream & os,const FmtSetupInactiveFunction & fmt)296 std::ostream &operator<<(std::ostream &os, const FmtSetupInactiveFunction &fmt)
297 {
298     if ((fmt.usage == FuncUsage::Call) && (fmt.partId == kNoPartId))
299     {
300         os << "if (gReplayResourceMode == angle::ReplayResourceMode::All)\n    {\n        ";
301     }
302     os << "SetupReplay";
303 
304     if (fmt.contextId != kNoContextId)
305     {
306         os << "Context";
307     }
308 
309     if (fmt.contextId == kSharedContextId)
310     {
311         os << "Shared";
312     }
313     else
314     {
315         os << fmt.contextId;
316     }
317 
318     os << "Inactive";
319 
320     if (fmt.partId != kNoPartId)
321     {
322         os << "Part" << fmt.partId;
323     }
324 
325     os << fmt.usage;
326 
327     if ((fmt.usage == FuncUsage::Call) && (fmt.partId == kNoPartId))
328     {
329         os << ";\n    }";
330     }
331     return os;
332 }
333 
operator <<(std::ostream & os,const FmtResetFunction & fmt)334 std::ostream &operator<<(std::ostream &os, const FmtResetFunction &fmt)
335 {
336     os << "ResetReplayContext";
337 
338     if (fmt.contextId == kSharedContextId)
339     {
340         os << "Shared";
341     }
342     else
343     {
344         os << fmt.contextId;
345     }
346 
347     if (fmt.partId != kNoPartId)
348     {
349         os << "Part" << fmt.partId;
350     }
351     os << fmt.usage;
352     return os;
353 }
354 
operator <<(std::ostream & os,const FmtFunction & fmt)355 std::ostream &operator<<(std::ostream &os, const FmtFunction &fmt)
356 {
357     switch (fmt.funcType)
358     {
359         case ReplayFunc::Replay:
360             os << FmtReplayFunction(fmt.contextId, fmt.usage, fmt.frameIndex, fmt.partId);
361             break;
362 
363         case ReplayFunc::Setup:
364             os << FmtSetupFunction(fmt.partId, fmt.contextId, fmt.usage);
365             break;
366 
367         case ReplayFunc::SetupInactive:
368             os << FmtSetupInactiveFunction(fmt.partId, fmt.contextId, fmt.usage);
369             break;
370 
371         case ReplayFunc::Reset:
372             os << FmtResetFunction(fmt.partId, fmt.contextId, fmt.usage);
373             break;
374 
375         case ReplayFunc::SetupFirstFrame:
376             os << FmtSetupFirstFrameFunction(fmt.partId);
377             break;
378 
379         default:
380             UNREACHABLE();
381             break;
382     }
383 
384     return os;
385 }
386 
operator <<(std::ostream & ostr,const FmtMultiLineString & fmt)387 std::ostream &operator<<(std::ostream &ostr, const FmtMultiLineString &fmt)
388 {
389     ASSERT(!fmt.strings.empty());
390     bool first = true;
391     for (const std::string &string : fmt.strings)
392     {
393         if (first)
394         {
395             first = false;
396         }
397         else
398         {
399             ostr << "\\n\"\n";
400         }
401 
402         ostr << "\"" << EscapeString(string);
403     }
404 
405     ostr << "\"";
406 
407     return ostr;
408 }
409 
GetDefaultOutDirectory()410 std::string GetDefaultOutDirectory()
411 {
412 #if defined(ANGLE_PLATFORM_ANDROID)
413     std::string path = "/sdcard/Android/data/";
414 
415     // Linux interface to get application id of the running process
416     FILE *cmdline = fopen("/proc/self/cmdline", "r");
417     char applicationId[512];
418     if (cmdline)
419     {
420         fread(applicationId, 1, sizeof(applicationId), cmdline);
421         fclose(cmdline);
422 
423         // Some package may have application id as <app_name>:<cmd_name>
424         char *colonSep = strchr(applicationId, ':');
425         if (colonSep)
426         {
427             *colonSep = '\0';
428         }
429     }
430     else
431     {
432         ERR() << "not able to lookup application id";
433     }
434 
435     constexpr char kAndroidOutputSubdir[] = "/angle_capture/";
436     path += std::string(applicationId) + kAndroidOutputSubdir;
437 
438     // Check for existence of output path
439     struct stat dir_stat;
440     if (stat(path.c_str(), &dir_stat) == -1)
441     {
442         ERR() << "Output directory '" << path
443               << "' does not exist.  Create it over adb using mkdir.";
444     }
445 
446     return path;
447 #else
448     return std::string("./");
449 #endif  // defined(ANGLE_PLATFORM_ANDROID)
450 }
451 
452 FrameCapture::FrameCapture()  = default;
453 FrameCapture::~FrameCapture() = default;
454 
reset()455 void FrameCapture::reset()
456 {
457     mSetupCalls.clear();
458 }
459 
FrameCaptureShared()460 FrameCaptureShared::FrameCaptureShared()
461     : mEnabled(true),
462       mSerializeStateEnabled(false),
463       mCompression(true),
464       mClientVertexArrayMap{},
465       mFrameIndex(1),
466       mCaptureStartFrame(1),
467       mCaptureEndFrame(0),
468       mClientArraySizes{},
469       mReadBufferSize(0),
470       mResourceIDBufferSize(0),
471       mHasResourceType{},
472       mResourceIDToSetupCalls{},
473       mMaxAccessedResourceIDs{},
474       mCaptureTrigger(0),
475       mCaptureActive(false),
476       mWindowSurfaceContextID({0})
477 {
478     reset();
479 
480     std::string enabledFromEnv =
481         GetEnvironmentVarOrUnCachedAndroidProperty(kEnabledVarName, kAndroidEnabled);
482     if (enabledFromEnv == "0")
483     {
484         mEnabled = false;
485     }
486 
487     std::string startFromEnv =
488         GetEnvironmentVarOrUnCachedAndroidProperty(kFrameStartVarName, kAndroidFrameStart);
489     if (!startFromEnv.empty())
490     {
491         mCaptureStartFrame = atoi(startFromEnv.c_str());
492     }
493     if (mCaptureStartFrame < 1)
494     {
495         WARN() << "Cannot use a capture start frame less than 1.";
496         mCaptureStartFrame = 1;
497     }
498 
499     std::string endFromEnv =
500         GetEnvironmentVarOrUnCachedAndroidProperty(kFrameEndVarName, kAndroidFrameEnd);
501     if (!endFromEnv.empty())
502     {
503         mCaptureEndFrame = atoi(endFromEnv.c_str());
504     }
505 
506     std::string captureTriggerFromEnv =
507         GetEnvironmentVarOrUnCachedAndroidProperty(kTriggerVarName, kAndroidTrigger);
508     if (!captureTriggerFromEnv.empty())
509     {
510         mCaptureTrigger = atoi(captureTriggerFromEnv.c_str());
511 
512         // Using capture trigger, initialize frame range variables for MEC
513         resetCaptureStartEndFrames();
514     }
515 
516     std::string labelFromEnv =
517         GetEnvironmentVarOrUnCachedAndroidProperty(kCaptureLabelVarName, kAndroidCaptureLabel);
518     // --angle-per-test-capture-label sets the env var, not properties
519     if (labelFromEnv.empty())
520     {
521         labelFromEnv = GetEnvironmentVar(kCaptureLabelVarName);
522     }
523     if (!labelFromEnv.empty())
524     {
525         // Optional label to provide unique file names and namespaces
526         mCaptureLabel = labelFromEnv;
527     }
528 
529     std::string compressionFromEnv =
530         GetEnvironmentVarOrUnCachedAndroidProperty(kCompressionVarName, kAndroidCompression);
531     if (compressionFromEnv == "0")
532     {
533         mCompression = false;
534     }
535     std::string serializeStateFromEnv = angle::GetEnvironmentVar(kSerializeStateVarName);
536     if (serializeStateFromEnv == "1")
537     {
538         mSerializeStateEnabled = true;
539     }
540 
541     std::string validateSerialiedStateFromEnv =
542         GetEnvironmentVarOrUnCachedAndroidProperty(kValidationVarName, kAndroidValidation);
543     if (validateSerialiedStateFromEnv == "1")
544     {
545         mValidateSerializedState = true;
546     }
547 
548     mValidationExpression =
549         GetEnvironmentVarOrUnCachedAndroidProperty(kValidationExprVarName, kAndroidValidationExpr);
550 
551     if (!mValidationExpression.empty())
552     {
553         INFO() << "Validation expression is " << kValidationExprVarName;
554     }
555 
556     // TODO: Remove. http://anglebug.com/42266223
557     std::string sourceExtFromEnv =
558         GetEnvironmentVarOrUnCachedAndroidProperty(kSourceExtVarName, kAndroidSourceExt);
559     if (!sourceExtFromEnv.empty())
560     {
561         if (sourceExtFromEnv == "c" || sourceExtFromEnv == "cpp")
562         {
563             mReplayWriter.setSourceFileExtension(sourceExtFromEnv.c_str());
564         }
565         else
566         {
567             WARN() << "Invalid capture source extension: " << sourceExtFromEnv;
568         }
569     }
570 
571     std::string sourceSizeFromEnv =
572         GetEnvironmentVarOrUnCachedAndroidProperty(kSourceSizeVarName, kAndroidSourceSize);
573     if (!sourceSizeFromEnv.empty())
574     {
575         int sourceSize = atoi(sourceSizeFromEnv.c_str());
576         if (sourceSize < 0)
577         {
578             WARN() << "Invalid capture source size: " << sourceSize;
579         }
580         else
581         {
582             mReplayWriter.setSourceFileSizeThreshold(sourceSize);
583         }
584     }
585 
586     std::string forceShadowFromEnv =
587         GetEnvironmentVarOrUnCachedAndroidProperty(kForceShadowVarName, kAndroidForceShadow);
588     if (forceShadowFromEnv == "1")
589     {
590         INFO() << "Force enabling shadow memory for coherent buffer tracking.";
591         mCoherentBufferTracker.enableShadowMemory();
592     }
593 
594     if (mFrameIndex == mCaptureStartFrame)
595     {
596         // Capture is starting from the first frame, so set the capture active to ensure all GLES
597         // commands issued are handled correctly by maybeCapturePreCallUpdates() and
598         // maybeCapturePostCallUpdates().
599         setCaptureActive();
600     }
601 
602     if (mCaptureEndFrame < mCaptureStartFrame)
603     {
604         // If we're still in a situation where start frame is after end frame,
605         // capture cannot happen. Consider this a disabled state.
606         // Note: We won't get here if trigger is in use, as it sets them equal but huge.
607         mEnabled = false;
608     }
609 
610     // Special case the output directory
611     if (mEnabled)
612     {
613         // Only perform output directory checks if enabled
614         // - This can avoid some expensive process name and filesystem checks
615         // - We want to emit errors if the directory doesn't exist
616         getOutputDirectory();
617     }
618 
619     mMaxCLParamsSize[ParamType::Tcl_device_idPointer]   = 0;
620     mMaxCLParamsSize[ParamType::Tcl_context]            = 0;
621     mMaxCLParamsSize[ParamType::Tcl_platform_idPointer] = 0;
622     mMaxCLParamsSize[ParamType::Tcl_command_queue]      = 0;
623     mMaxCLParamsSize[ParamType::Tcl_program]            = 0;
624     mMaxCLParamsSize[ParamType::Tcl_kernel]             = 0;
625     mMaxCLParamsSize[ParamType::Tcl_mem]                = 0;
626     mMaxCLParamsSize[ParamType::Tcl_eventPointer]       = 0;
627     mMaxCLParamsSize[ParamType::Tcl_sampler]            = 0;
628     mMaxCLParamsSize[ParamType::TvoidPointer]           = 0;
629 }
630 
~FrameCaptureShared()631 FrameCaptureShared::~FrameCaptureShared() {}
632 
isCapturing() const633 bool FrameCaptureShared::isCapturing() const
634 {
635     // Currently we will always do a capture up until the last frame. In the future we could improve
636     // mid execution capture by only capturing between the start and end frames. The only necessary
637     // reason we need to capture before the start is for attached program and shader sources.
638     return mEnabled;
639 }
640 
getFrameCount() const641 uint32_t FrameCaptureShared::getFrameCount() const
642 {
643     return mCaptureEndFrame - mCaptureStartFrame + 1;
644 }
645 
getReplayFrameIndex() const646 uint32_t FrameCaptureShared::getReplayFrameIndex() const
647 {
648     return mFrameIndex - mCaptureStartFrame + 1;
649 }
650 
isRuntimeEnabled()651 bool FrameCaptureShared::isRuntimeEnabled()
652 {
653     if (!mRuntimeEnabled && mRuntimeInitialized)
654     {
655         return false;
656     }
657     if (mRuntimeEnabled)
658     {
659         return true;
660     }
661 
662     uint32_t mCaptureStartFrame = 1;
663     uint32_t mCaptureEndFrame   = 0;
664     std::string enabledFromEnv =
665         GetEnvironmentVarOrUnCachedAndroidProperty(kEnabledVarName, kAndroidEnabled);
666 
667     std::string startFromEnv =
668         GetEnvironmentVarOrUnCachedAndroidProperty(kFrameStartVarName, kAndroidFrameStart);
669     if (!startFromEnv.empty())
670     {
671         mCaptureStartFrame = atoi(startFromEnv.c_str());
672     }
673     if (mCaptureStartFrame < 1)
674     {
675         mCaptureStartFrame = 1;
676     }
677 
678     std::string endFromEnv =
679         GetEnvironmentVarOrUnCachedAndroidProperty(kFrameEndVarName, kAndroidFrameEnd);
680     if (!endFromEnv.empty())
681     {
682         mCaptureEndFrame = atoi(endFromEnv.c_str());
683     }
684 
685     uint32_t mCaptureTrigger = 0;
686     std::string captureTriggerFromEnv =
687         GetEnvironmentVarOrUnCachedAndroidProperty(kTriggerVarName, kAndroidTrigger);
688     if (!captureTriggerFromEnv.empty())
689     {
690         mCaptureTrigger = atoi(captureTriggerFromEnv.c_str());
691     }
692 
693     mRuntimeEnabled =
694         enabledFromEnv != "0" &&
695         (mCaptureTrigger || (mCaptureEndFrame != 0 && mCaptureEndFrame >= mCaptureStartFrame));
696 
697     mRuntimeInitialized = true;
698     return mRuntimeEnabled;
699 }
700 
reset()701 void FrameCaptureShared::reset()
702 {
703     mFrameCalls.clear();
704     mClientVertexArrayMap.fill(-1);
705 
706     // Do not reset replay-specific settings like the maximum read buffer size, client array sizes,
707     // or the 'has seen' type map. We could refine this into per-frame and per-capture maximums if
708     // necessary.
709 }
710 
711 // This function will clear FrameCaptureShared state so that mid-execution capture can be
712 // run multiple times.
resetMidExecutionCapture(gl::Context * context)713 void FrameCaptureShared::resetMidExecutionCapture(gl::Context *context)
714 {
715     for (ResourceIDType resourceID : AllEnums<ResourceIDType>())
716     {
717         mResourceIDToSetupCalls[resourceID].clear();
718     }
719 
720     egl::ShareGroup *shareGroup = context->getShareGroup();
721     for (auto shareContext : shareGroup->getContexts())
722     {
723         FrameCapture *frameCapture = shareContext.second->getFrameCapture();
724         frameCapture->reset();
725         frameCapture->getStateResetHelper().reset();
726     }
727 
728     mActiveFrameIndices.clear();
729     mWroteIndexFile = false;
730     std::fill(std::begin(mClientArraySizes), std::end(mClientArraySizes), 0);
731     mReadBufferSize       = 0;
732     mResourceIDBufferSize = 0;
733     mHasResourceType.Zero();
734     mBufferDataMap.clear();
735     mMaxAccessedResourceIDs.fill(0);
736     mResourceTracker.resetResourceTracking();
737     mReplayWriter.reset();
738     mShareGroupSetupCalls.clear();
739     mDeferredLinkPrograms.clear();
740     mActiveContexts.clear();
741 }
742 
743 // ReplayWriter implementation.
ReplayWriter()744 ReplayWriter::ReplayWriter()
745     : mSourceFileExtension(kDefaultSourceFileExt),
746       mSourceFileSizeThreshold(kDefaultSourceFileSizeThreshold),
747       mFrameIndex(1)
748 {}
749 
~ReplayWriter()750 ReplayWriter::~ReplayWriter()
751 {
752     ASSERT(mPrivateFunctionPrototypes.empty());
753     ASSERT(mPublicFunctionPrototypes.empty());
754     ASSERT(mPrivateFunctions.empty());
755     ASSERT(mPublicFunctions.empty());
756     ASSERT(mGlobalVariableDeclarations.empty());
757     ASSERT(mStaticVariableDeclarations.empty());
758     ASSERT(mReplayHeaders.empty());
759 }
760 
setSourceFileExtension(const char * ext)761 void ReplayWriter::setSourceFileExtension(const char *ext)
762 {
763     mSourceFileExtension = ext;
764 }
765 
setSourceFileSizeThreshold(size_t sourceFileSizeThreshold)766 void ReplayWriter::setSourceFileSizeThreshold(size_t sourceFileSizeThreshold)
767 {
768     mSourceFileSizeThreshold = sourceFileSizeThreshold;
769 }
770 
setFilenamePattern(const std::string & pattern)771 void ReplayWriter::setFilenamePattern(const std::string &pattern)
772 {
773     if (mFilenamePattern != pattern)
774     {
775         mFilenamePattern = pattern;
776     }
777 }
778 
setSourcePrologue(const std::string & prologue)779 void ReplayWriter::setSourcePrologue(const std::string &prologue)
780 {
781     mSourcePrologue = prologue;
782 }
783 
setHeaderPrologue(const std::string & prologue)784 void ReplayWriter::setHeaderPrologue(const std::string &prologue)
785 {
786     mHeaderPrologue = prologue;
787 }
788 
addPublicFunction(const std::string & functionProto,const std::stringstream & headerStream,const std::stringstream & bodyStream)789 void ReplayWriter::addPublicFunction(const std::string &functionProto,
790                                      const std::stringstream &headerStream,
791                                      const std::stringstream &bodyStream)
792 {
793     mPublicFunctionPrototypes.push_back(functionProto);
794 
795     std::string header = headerStream.str();
796     std::string body   = bodyStream.str();
797 
798     if (!header.empty())
799     {
800         mReplayHeaders.emplace_back(header);
801     }
802 
803     if (!body.empty())
804     {
805         mPublicFunctions.emplace_back(body);
806     }
807 }
808 
addPrivateFunction(const std::string & functionProto,const std::stringstream & headerStream,const std::stringstream & bodyStream)809 void ReplayWriter::addPrivateFunction(const std::string &functionProto,
810                                       const std::stringstream &headerStream,
811                                       const std::stringstream &bodyStream)
812 {
813     mPrivateFunctionPrototypes.push_back(functionProto);
814 
815     std::string header = headerStream.str();
816     std::string body   = bodyStream.str();
817 
818     if (!header.empty())
819     {
820         mReplayHeaders.emplace_back(header);
821     }
822 
823     if (!body.empty())
824     {
825         mPrivateFunctions.emplace_back(body);
826     }
827 }
828 
getInlineVariableName(EntryPoint entryPoint,const std::string & paramName)829 std::string ReplayWriter::getInlineVariableName(EntryPoint entryPoint, const std::string &paramName)
830 {
831     int counter = mDataTracker.getCounters().getAndIncrement(entryPoint, paramName);
832     return GetVarName(entryPoint, paramName, counter);
833 }
834 
getAndIncrement(EntryPoint entryPoint,const std::string & paramName)835 int DataCounters::getAndIncrement(EntryPoint entryPoint, const std::string &paramName)
836 {
837     Counter counterKey = {entryPoint, paramName};
838     return mData[counterKey]++;
839 }
840 
getStringCounter(const std::vector<std::string> & strings)841 int StringCounters::getStringCounter(const std::vector<std::string> &strings)
842 {
843     const auto &id = mStringCounterMap.find(strings);
844     if (id == mStringCounterMap.end())
845     {
846         return kStringsNotFound;
847     }
848     else
849     {
850         return mStringCounterMap[strings];
851     }
852 }
853 
setStringCounter(const std::vector<std::string> & strings,int & counter)854 void StringCounters::setStringCounter(const std::vector<std::string> &strings, int &counter)
855 {
856     ASSERT(counter >= 0);
857     mStringCounterMap[strings] = counter;
858 }
859 
860 StringCounters::StringCounters() = default;
861 
862 StringCounters::~StringCounters() = default;
863 
864 DataCounters::DataCounters() = default;
865 
866 DataCounters::~DataCounters() = default;
867 
868 DataTracker::DataTracker() = default;
869 
870 DataTracker::~DataTracker() = default;
871 
getInlineStringSetVariableName(EntryPoint entryPoint,const std::string & paramName,const std::vector<std::string> & strings,bool * isNewEntryOut)872 std::string ReplayWriter::getInlineStringSetVariableName(EntryPoint entryPoint,
873                                                          const std::string &paramName,
874                                                          const std::vector<std::string> &strings,
875                                                          bool *isNewEntryOut)
876 {
877     int counter    = mDataTracker.getStringCounters().getStringCounter(strings);
878     *isNewEntryOut = (counter == kStringsNotFound);
879     if (*isNewEntryOut)
880     {
881         // This is a unique set of strings, so set up their declaration and update the counter
882         counter = mDataTracker.getCounters().getAndIncrement(entryPoint, paramName);
883         mDataTracker.getStringCounters().setStringCounter(strings, counter);
884 
885         std::string varName = GetVarName(entryPoint, paramName, counter);
886 
887         std::stringstream declStream;
888         declStream << "const char *" << (captureAPI == CaptureAPI::CL ? " " : "const ") << varName
889                    << "[]";
890         std::string decl = declStream.str();
891 
892         mGlobalVariableDeclarations.push_back(decl);
893 
894         return varName;
895     }
896     else
897     {
898         return GetVarName(entryPoint, paramName, counter);
899     }
900 }
901 
addStaticVariable(const std::string & customVarType,const std::string & customVarName)902 void ReplayWriter::addStaticVariable(const std::string &customVarType,
903                                      const std::string &customVarName)
904 {
905     std::string decl = customVarType + " " + customVarName;
906     mStaticVariableDeclarations.push_back(decl);
907 }
908 
getStoredReplaySourceSize() const909 size_t ReplayWriter::getStoredReplaySourceSize() const
910 {
911     size_t sum = 0;
912     for (const std::string &header : mReplayHeaders)
913     {
914         sum += header.size();
915     }
916     for (const std::string &publicFunc : mPublicFunctions)
917     {
918         sum += publicFunc.size();
919     }
920     for (const std::string &privateFunc : mPrivateFunctions)
921     {
922         sum += privateFunc.size();
923     }
924     return sum;
925 }
926 
927 // static
GetVarName(EntryPoint entryPoint,const std::string & paramName,int counter)928 std::string ReplayWriter::GetVarName(EntryPoint entryPoint,
929                                      const std::string &paramName,
930                                      int counter)
931 {
932     std::stringstream strstr;
933     strstr << GetEntryPointName(entryPoint) << "_" << paramName << "_" << counter;
934     return strstr.str();
935 }
936 
saveFrame()937 void ReplayWriter::saveFrame()
938 {
939     if (mReplayHeaders.empty() && mPublicFunctions.empty() && mPrivateFunctions.empty())
940     {
941         return;
942     }
943 
944     ASSERT(!mSourceFileExtension.empty());
945 
946     std::stringstream strstr;
947     strstr << mFilenamePattern << "_" << std::setfill('0') << std::setw(3) << mFrameIndex << "."
948            << mSourceFileExtension;
949 
950     std::string frameFilePath = strstr.str();
951 
952     if (captureAPI == CaptureAPI::GL)
953     {
954         ++mFrameIndex;
955     }
956 
957     writeReplaySource(frameFilePath);
958 }
959 
saveFrameIfFull()960 void ReplayWriter::saveFrameIfFull()
961 {
962     if (getStoredReplaySourceSize() < mSourceFileSizeThreshold)
963     {
964         INFO() << "Merging captured frame: " << getStoredReplaySourceSize()
965                << " less than threshold of " << mSourceFileSizeThreshold << " bytes";
966         return;
967     }
968 
969     saveFrame();
970 }
971 
saveHeader()972 void ReplayWriter::saveHeader()
973 {
974     std::stringstream headerPathStream;
975     headerPathStream << mFilenamePattern << ".h";
976     std::string headerPath = headerPathStream.str();
977 
978     SaveFileHelper saveH(headerPath);
979 
980     saveH << mHeaderPrologue << "\n";
981 
982     saveH << "// Public functions are declared in "
983           << (captureAPI == CaptureAPI::GL ? "trace_fixture.h.\n" : "trace_fixture_cl.h.\n");
984     saveH << "\n";
985     saveH << "// Private Functions\n";
986     saveH << "\n";
987 
988     for (const std::string &proto : mPrivateFunctionPrototypes)
989     {
990         saveH << proto << ";\n";
991     }
992 
993     saveH << "\n";
994     saveH << "// Global variables\n";
995     saveH << "\n";
996 
997     for (const std::string &globalVar : mGlobalVariableDeclarations)
998     {
999         saveH << "extern " << globalVar << ";\n";
1000     }
1001 
1002     for (const std::string &staticVar : mStaticVariableDeclarations)
1003     {
1004         saveH << "static " << staticVar << ";\n";
1005     }
1006 
1007     mPublicFunctionPrototypes.clear();
1008     mPrivateFunctionPrototypes.clear();
1009     mGlobalVariableDeclarations.clear();
1010     mStaticVariableDeclarations.clear();
1011 
1012     addWrittenFile(headerPath);
1013 }
1014 
saveIndexFilesAndHeader()1015 void ReplayWriter::saveIndexFilesAndHeader()
1016 {
1017     ASSERT(!mSourceFileExtension.empty());
1018 
1019     std::stringstream sourcePathStream;
1020     sourcePathStream << mFilenamePattern << "." << mSourceFileExtension;
1021     std::string sourcePath = sourcePathStream.str();
1022 
1023     writeReplaySource(sourcePath);
1024     saveHeader();
1025 }
1026 
saveSetupFile()1027 void ReplayWriter::saveSetupFile()
1028 {
1029     ASSERT(!mSourceFileExtension.empty());
1030 
1031     std::stringstream strstr;
1032     strstr << mFilenamePattern << "." << mSourceFileExtension;
1033 
1034     std::string frameFilePath = strstr.str();
1035 
1036     writeReplaySource(frameFilePath);
1037 }
1038 
writeReplaySource(const std::string & filename)1039 void ReplayWriter::writeReplaySource(const std::string &filename)
1040 {
1041     SaveFileHelper saveCpp(filename);
1042 
1043     saveCpp << mSourcePrologue << "\n";
1044     for (const std::string &header : mReplayHeaders)
1045     {
1046         saveCpp << header << "\n";
1047     }
1048 
1049     saveCpp << "// Private Functions\n";
1050     saveCpp << "\n";
1051 
1052     for (const std::string &func : mPrivateFunctions)
1053     {
1054         saveCpp << func << "\n";
1055     }
1056 
1057     saveCpp << "// Public Functions\n";
1058     saveCpp << "\n";
1059 
1060     if (mFilenamePattern == "cpp")
1061     {
1062         saveCpp << "extern \"C\"\n";
1063         saveCpp << "{\n";
1064     }
1065 
1066     for (const std::string &func : mPublicFunctions)
1067     {
1068         saveCpp << func << "\n";
1069     }
1070 
1071     if (mFilenamePattern == "cpp")
1072     {
1073         saveCpp << "}  // extern \"C\"\n";
1074     }
1075 
1076     mReplayHeaders.clear();
1077     mPrivateFunctions.clear();
1078     mPublicFunctions.clear();
1079 
1080     addWrittenFile(filename);
1081 }
1082 
GetBaseName(const std::string & nameWithPath)1083 std::string GetBaseName(const std::string &nameWithPath)
1084 {
1085     std::vector<std::string> result = angle::SplitString(
1086         nameWithPath, "/\\", WhitespaceHandling::TRIM_WHITESPACE, SplitResult::SPLIT_WANT_NONEMPTY);
1087     ASSERT(!result.empty());
1088     return result.back();
1089 }
1090 
addWrittenFile(const std::string & filename)1091 void ReplayWriter::addWrittenFile(const std::string &filename)
1092 {
1093     std::string writtenFile = GetBaseName(filename);
1094     ASSERT(std::find(mWrittenFiles.begin(), mWrittenFiles.end(), writtenFile) ==
1095            mWrittenFiles.end());
1096     mWrittenFiles.push_back(writtenFile);
1097 }
1098 
getAndResetWrittenFiles()1099 std::vector<std::string> ReplayWriter::getAndResetWrittenFiles()
1100 {
1101     std::vector<std::string> results = std::move(mWrittenFiles);
1102     std::sort(results.begin(), results.end());
1103     ASSERT(mWrittenFiles.empty());
1104     return results;
1105 }
1106 
AddComment(std::vector<CallCapture> * outCalls,const std::string & comment)1107 void AddComment(std::vector<CallCapture> *outCalls, const std::string &comment)
1108 {
1109     ParamBuffer commentParamBuffer;
1110     ParamCapture commentParam("comment", ParamType::TGLcharConstPointer);
1111     CaptureString(comment.c_str(), &commentParam);
1112     commentParamBuffer.addParam(std::move(commentParam));
1113     outCalls->emplace_back("Comment", std::move(commentParamBuffer));
1114 }
1115 
1116 bool FrameCaptureShared::mRuntimeEnabled     = false;
1117 bool FrameCaptureShared::mRuntimeInitialized = false;
1118 
getOutputDirectory()1119 void FrameCaptureShared::getOutputDirectory()
1120 {
1121     std::string pathFromEnv =
1122         GetEnvironmentVarOrUnCachedAndroidProperty(kOutDirectoryVarName, kAndroidOutDir);
1123     if (pathFromEnv.empty())
1124     {
1125         mOutDirectory = GetDefaultOutDirectory();
1126     }
1127     else
1128     {
1129         mOutDirectory = pathFromEnv;
1130     }
1131 
1132     // Ensure the capture path ends with a slash.
1133     if (mOutDirectory.back() != '\\' && mOutDirectory.back() != '/')
1134     {
1135         mOutDirectory += '/';
1136     }
1137 }
1138 
CaptureMemory(const void * source,size_t size,ParamCapture * paramCapture)1139 void CaptureMemory(const void *source, size_t size, ParamCapture *paramCapture)
1140 {
1141     std::vector<uint8_t> data(size);
1142     memcpy(data.data(), source, size);
1143     paramCapture->data.emplace_back(std::move(data));
1144 }
1145 
CaptureString(const GLchar * str,ParamCapture * paramCapture)1146 void CaptureString(const GLchar *str, ParamCapture *paramCapture)
1147 {
1148     // include the '\0' suffix
1149     CaptureMemory(str, strlen(str) + 1, paramCapture);
1150 }
1151 
1152 TrackedResource::TrackedResource() = default;
1153 
1154 TrackedResource::~TrackedResource() = default;
1155 
1156 ResourceTracker::ResourceTracker() = default;
1157 
1158 ResourceTracker::~ResourceTracker() = default;
1159 
1160 StateResetHelper::StateResetHelper() = default;
1161 
1162 StateResetHelper::~StateResetHelper() = default;
1163 
CoherentBufferTracker()1164 CoherentBufferTracker::CoherentBufferTracker()
1165     : mEnabled(false), mHasBeenReset(false), mShadowMemoryEnabled(false)
1166 {
1167     mPageSize = GetPageSize();
1168 }
1169 
~CoherentBufferTracker()1170 CoherentBufferTracker::~CoherentBufferTracker()
1171 {
1172     disable();
1173 }
1174 
disable()1175 void CoherentBufferTracker::disable()
1176 {
1177     if (!mEnabled)
1178     {
1179         return;
1180     }
1181 
1182     if (mPageFaultHandler->disable())
1183     {
1184         mEnabled = false;
1185     }
1186     else
1187     {
1188         ERR() << "Could not disable page fault handler.";
1189     }
1190 
1191     if (mShadowMemoryEnabled && mBuffers.size() > 0)
1192     {
1193         WARN() << "Disabling coherent buffer tracking while leaving shadow memory without "
1194                   "synchronization. Expect rendering artifacts after capture ends.";
1195     }
1196 }
1197 
1198 }  // namespace angle
1199