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.h:
7 // ANGLE Frame capture interface.
8 //
9
10 #ifndef LIBANGLE_FRAME_CAPTURE_H_
11 #define LIBANGLE_FRAME_CAPTURE_H_
12
13 #include <fstream>
14 #include "sys/stat.h"
15
16 #include "common/PackedEnums.h"
17 #include "common/SimpleMutex.h"
18 #include "common/frame_capture_utils.h"
19 #include "common/string_utils.h"
20 #include "common/system_utils.h"
21 #include "libANGLE/Context.h"
22 #include "libANGLE/ShareGroup.h"
23 #include "libANGLE/Thread.h"
24 #include "libANGLE/angletypes.h"
25 #include "libANGLE/entry_points_utils.h"
26
27 #ifdef ANGLE_ENABLE_CL
28 # include "libANGLE/CLPlatform.h"
29 #endif
30
31 namespace gl
32 {
33 enum class BigGLEnum;
34 enum class GLESEnum;
35 } // namespace gl
36
37 namespace angle
38 {
39 // Helper to use unique IDs for each local data variable.
40 class DataCounters final : angle::NonCopyable
41 {
42 public:
43 DataCounters();
44 ~DataCounters();
45
46 int getAndIncrement(EntryPoint entryPoint, const std::string ¶mName);
reset()47 void reset() { mData.clear(); }
48
49 private:
50 // <CallName, ParamName>
51 using Counter = std::pair<EntryPoint, std::string>;
52 std::map<Counter, int> mData;
53 };
54
55 constexpr int kStringsNotFound = -1;
56 class StringCounters final : angle::NonCopyable
57 {
58 public:
59 StringCounters();
60 ~StringCounters();
61
62 int getStringCounter(const std::vector<std::string> &str);
63 void setStringCounter(const std::vector<std::string> &str, int &counter);
reset()64 void reset() { mStringCounterMap.clear(); }
65
66 private:
67 std::map<std::vector<std::string>, int> mStringCounterMap;
68 };
69
70 class DataTracker final : angle::NonCopyable
71 {
72 public:
73 DataTracker();
74 ~DataTracker();
75
getCounters()76 DataCounters &getCounters() { return mCounters; }
getStringCounters()77 StringCounters &getStringCounters() { return mStringCounters; }
78
reset()79 void reset()
80 {
81 mCounters.reset();
82 mStringCounters.reset();
83 }
84
85 private:
86 DataCounters mCounters;
87 StringCounters mStringCounters;
88 };
89
90 enum class CaptureAPI : uint8_t
91 {
92 GL = 0,
93 CL = 1,
94
95 InvalidEnum = 2,
96 EnumCount = 2,
97 };
98
99 class ReplayWriter final : angle::NonCopyable
100 {
101 public:
102 ReplayWriter();
103 ~ReplayWriter();
104
105 void setSourceFileExtension(const char *ext);
106 void setSourceFileSizeThreshold(size_t sourceFileSizeThreshold);
107 void setFilenamePattern(const std::string &pattern);
108 void setSourcePrologue(const std::string &prologue);
109 void setHeaderPrologue(const std::string &prologue);
110
111 void addPublicFunction(const std::string &functionProto,
112 const std::stringstream &headerStream,
113 const std::stringstream &bodyStream);
114 void addPrivateFunction(const std::string &functionProto,
115 const std::stringstream &headerStream,
116 const std::stringstream &bodyStream);
117 std::string getInlineVariableName(EntryPoint entryPoint, const std::string ¶mName);
118
119 std::string getInlineStringSetVariableName(EntryPoint entryPoint,
120 const std::string ¶mName,
121 const std::vector<std::string> &strings,
122 bool *isNewEntryOut);
123
124 void addStaticVariable(const std::string &customVarType, const std::string &customVarName);
125
126 void saveFrame();
127 void saveFrameIfFull();
128 void saveIndexFilesAndHeader();
129 void saveSetupFile();
130
131 std::vector<std::string> getAndResetWrittenFiles();
132
reset()133 void reset()
134 {
135 mDataTracker.reset();
136 mFrameIndex = 1; // This is really the FileIndex
137 }
138
139 CaptureAPI captureAPI = CaptureAPI::GL;
140
141 private:
142 static std::string GetVarName(EntryPoint entryPoint, const std::string ¶mName, int counter);
143
144 void saveHeader();
145 void writeReplaySource(const std::string &filename);
146 void addWrittenFile(const std::string &filename);
147 size_t getStoredReplaySourceSize() const;
148
149 std::string mSourceFileExtension;
150 size_t mSourceFileSizeThreshold;
151 size_t mFrameIndex;
152
153 DataTracker mDataTracker;
154 std::string mFilenamePattern;
155 std::string mSourcePrologue;
156 std::string mHeaderPrologue;
157
158 std::vector<std::string> mReplayHeaders;
159 std::vector<std::string> mGlobalVariableDeclarations;
160 std::vector<std::string> mStaticVariableDeclarations;
161
162 std::vector<std::string> mPublicFunctionPrototypes;
163 std::vector<std::string> mPublicFunctions;
164
165 std::vector<std::string> mPrivateFunctionPrototypes;
166 std::vector<std::string> mPrivateFunctions;
167
168 std::vector<std::string> mWrittenFiles;
169 };
170
171 using BufferCalls = std::map<GLuint, std::vector<CallCapture>>;
172
173 // true means mapped, false means unmapped
174 using BufferMapStatusMap = std::map<GLuint, bool>;
175
176 using FenceSyncSet = std::set<gl::SyncID>;
177 using FenceSyncCalls = std::map<gl::SyncID, std::vector<CallCapture>>;
178
179 // For default uniforms, we need to track which ones are dirty, and the series of calls to reset.
180 // Each program has unique default uniforms, and each uniform has one or more locations in the
181 // default buffer. For reset efficiency, we track only the uniforms dirty by location, per program.
182
183 // A set of all default uniforms (per program) that were modified during the run
184 using DefaultUniformLocationsSet = std::set<gl::UniformLocation>;
185 using DefaultUniformLocationsPerProgramMap =
186 std::map<gl::ShaderProgramID, DefaultUniformLocationsSet>;
187
188 // A map of programs which maps to locations and their reset calls
189 using DefaultUniformCallsPerLocationMap = std::map<gl::UniformLocation, std::vector<CallCapture>>;
190 using DefaultUniformCallsPerProgramMap =
191 std::map<gl::ShaderProgramID, DefaultUniformCallsPerLocationMap>;
192
193 using DefaultUniformBaseLocationMap =
194 std::map<std::pair<gl::ShaderProgramID, gl::UniformLocation>, gl::UniformLocation>;
195
196 using ResourceSet = std::set<GLuint>;
197 using ResourceCalls = std::map<GLuint, std::vector<CallCapture>>;
198
199 class TrackedResource final : angle::NonCopyable
200 {
201 public:
202 TrackedResource();
203 ~TrackedResource();
204
getStartingResources()205 const ResourceSet &getStartingResources() const { return mStartingResources; }
getStartingResources()206 ResourceSet &getStartingResources() { return mStartingResources; }
getNewResources()207 const ResourceSet &getNewResources() const { return mNewResources; }
getNewResources()208 ResourceSet &getNewResources() { return mNewResources; }
getResourcesToDelete()209 const ResourceSet &getResourcesToDelete() const { return mResourcesToDelete; }
getResourcesToDelete()210 ResourceSet &getResourcesToDelete() { return mResourcesToDelete; }
getResourcesToRegen()211 const ResourceSet &getResourcesToRegen() const { return mResourcesToRegen; }
getResourcesToRegen()212 ResourceSet &getResourcesToRegen() { return mResourcesToRegen; }
getResourcesToRestore()213 const ResourceSet &getResourcesToRestore() const { return mResourcesToRestore; }
getResourcesToRestore()214 ResourceSet &getResourcesToRestore() { return mResourcesToRestore; }
215
216 void setGennedResource(GLuint id);
217 void setDeletedResource(GLuint id);
218 void setModifiedResource(GLuint id);
219 bool resourceIsGenerated(GLuint id);
220
getResourceRegenCalls()221 ResourceCalls &getResourceRegenCalls() { return mResourceRegenCalls; }
getResourceRestoreCalls()222 ResourceCalls &getResourceRestoreCalls() { return mResourceRestoreCalls; }
223
reset()224 void reset()
225 {
226 mResourceRegenCalls.clear();
227 mResourceRestoreCalls.clear();
228 mStartingResources.clear();
229 mNewResources.clear();
230 mResourcesToDelete.clear();
231 mResourcesToRegen.clear();
232 mResourcesToRestore.clear();
233 }
234
235 private:
236 // Resource regen calls will gen a resource
237 ResourceCalls mResourceRegenCalls;
238 // Resource restore calls will restore the contents of a resource
239 ResourceCalls mResourceRestoreCalls;
240
241 // Resources created during startup
242 ResourceSet mStartingResources;
243
244 // Resources created during the run that need to be deleted
245 ResourceSet mNewResources;
246 // Resources recreated during the run that need to be deleted
247 ResourceSet mResourcesToDelete;
248 // Resources deleted during the run that need to be recreated
249 ResourceSet mResourcesToRegen;
250 // Resources modified during the run that need to be restored
251 ResourceSet mResourcesToRestore;
252 };
253
254 using TrackedResourceArray =
255 std::array<TrackedResource, static_cast<uint32_t>(ResourceIDType::EnumCount)>;
256
257 enum class ShaderProgramType
258 {
259 ShaderType,
260 ProgramType
261 };
262
263 // Helper to track resource changes during the capture
264 class ResourceTracker final : angle::NonCopyable
265 {
266 public:
267 ResourceTracker();
268 ~ResourceTracker();
269
getBufferMapCalls()270 BufferCalls &getBufferMapCalls() { return mBufferMapCalls; }
getBufferUnmapCalls()271 BufferCalls &getBufferUnmapCalls() { return mBufferUnmapCalls; }
272
getBufferBindingCalls()273 std::vector<CallCapture> &getBufferBindingCalls() { return mBufferBindingCalls; }
274
275 void setBufferMapped(gl::ContextID contextID, GLuint id);
276 void setBufferUnmapped(gl::ContextID contextID, GLuint id);
277
278 bool getStartingBuffersMappedCurrent(GLuint id) const;
279 bool getStartingBuffersMappedInitial(GLuint id) const;
280
setStartingBufferMapped(GLuint id,bool mapped)281 void setStartingBufferMapped(GLuint id, bool mapped)
282 {
283 // Track the current state (which will change throughout the trace)
284 mStartingBuffersMappedCurrent[id] = mapped;
285
286 // And the initial state, to compare during frame loop reset
287 mStartingBuffersMappedInitial[id] = mapped;
288 }
289
290 void onShaderProgramAccess(gl::ShaderProgramID shaderProgramID);
getMaxShaderPrograms()291 uint32_t getMaxShaderPrograms() const { return mMaxShaderPrograms; }
292
getStartingFenceSyncs()293 FenceSyncSet &getStartingFenceSyncs() { return mStartingFenceSyncs; }
getFenceSyncRegenCalls()294 FenceSyncCalls &getFenceSyncRegenCalls() { return mFenceSyncRegenCalls; }
getFenceSyncsToRegen()295 FenceSyncSet &getFenceSyncsToRegen() { return mFenceSyncsToRegen; }
296 void setDeletedFenceSync(gl::SyncID sync);
297
getDefaultUniformsToReset()298 DefaultUniformLocationsPerProgramMap &getDefaultUniformsToReset()
299 {
300 return mDefaultUniformsToReset;
301 }
getDefaultUniformResetCalls(gl::ShaderProgramID id)302 DefaultUniformCallsPerLocationMap &getDefaultUniformResetCalls(gl::ShaderProgramID id)
303 {
304 return mDefaultUniformResetCalls[id];
305 }
306 void setModifiedDefaultUniform(gl::ShaderProgramID programID, gl::UniformLocation location);
307 void setDefaultUniformBaseLocation(gl::ShaderProgramID programID,
308 gl::UniformLocation location,
309 gl::UniformLocation baseLocation);
getDefaultUniformBaseLocation(gl::ShaderProgramID programID,gl::UniformLocation location)310 gl::UniformLocation getDefaultUniformBaseLocation(gl::ShaderProgramID programID,
311 gl::UniformLocation location)
312 {
313 ASSERT(mDefaultUniformBaseLocations.find({programID, location}) !=
314 mDefaultUniformBaseLocations.end());
315 return mDefaultUniformBaseLocations[{programID, location}];
316 }
317
318 TrackedResource &getTrackedResource(gl::ContextID contextID, ResourceIDType type);
319
320 void getContextIDs(std::set<gl::ContextID> &idsOut);
321
getImageToAttribTable()322 std::map<EGLImage, egl::AttributeMap> &getImageToAttribTable() { return mMatchImageToAttribs; }
323
getTextureIDToImageTable()324 std::map<GLuint, egl::ImageID> &getTextureIDToImageTable() { return mMatchTextureIDToImage; }
325
setShaderProgramType(gl::ShaderProgramID id,angle::ShaderProgramType type)326 void setShaderProgramType(gl::ShaderProgramID id, angle::ShaderProgramType type)
327 {
328 mShaderProgramType[id] = type;
329 }
getShaderProgramType(gl::ShaderProgramID id)330 ShaderProgramType getShaderProgramType(gl::ShaderProgramID id)
331 {
332 ASSERT(mShaderProgramType.find(id) != mShaderProgramType.end());
333 return mShaderProgramType[id];
334 }
335
resetTrackedResourceArray(TrackedResourceArray & trackedResourceArray)336 void resetTrackedResourceArray(TrackedResourceArray &trackedResourceArray)
337 {
338 for (auto &trackedResource : trackedResourceArray)
339 {
340 trackedResource.reset();
341 }
342 }
343
344 // Some data in FrameCaptureShared tracks resources across all captures while
345 // other data is tracked per-capture. This function is responsible for
346 // resetting the per-capture tracking data in this class.
resetResourceTracking()347 void resetResourceTracking()
348 {
349 resetTrackedResourceArray(mTrackedResourcesShared);
350 for (auto &pair : mTrackedResourcesPerContext)
351 {
352 resetTrackedResourceArray(pair.second);
353 }
354
355 mBufferMapCalls.clear();
356 mBufferUnmapCalls.clear();
357 mBufferBindingCalls.clear();
358 mStartingBuffersMappedInitial.clear();
359 mStartingBuffersMappedCurrent.clear();
360 mMaxShaderPrograms = 0;
361 mStartingFenceSyncs.clear();
362 mFenceSyncRegenCalls.clear();
363 mFenceSyncsToRegen.clear();
364 mDefaultUniformsToReset.clear();
365 mDefaultUniformResetCalls.clear();
366 mDefaultUniformBaseLocations.clear();
367 }
368
369 private:
370 // These data structures are per-capture and must be reset before beginning a new
371 // capture in the resetResourceTracking() function above
372
373 // Buffer map calls will map a buffer with correct offset, length, and access flags
374 BufferCalls mBufferMapCalls;
375 // Buffer unmap calls will bind and unmap a given buffer
376 BufferCalls mBufferUnmapCalls;
377 // Buffer binding calls to restore bindings recorded during MEC
378 std::vector<CallCapture> mBufferBindingCalls;
379 // Whether a given buffer was mapped at the start of the trace
380 BufferMapStatusMap mStartingBuffersMappedInitial;
381 // The status of buffer mapping throughout the trace, modified with each Map/Unmap call
382 BufferMapStatusMap mStartingBuffersMappedCurrent;
383 // Maximum accessed shader program ID.
384 uint32_t mMaxShaderPrograms = 0;
385 // Fence sync objects created during MEC setup
386 FenceSyncSet mStartingFenceSyncs;
387 // Fence sync regen calls will create a fence sync objects
388 FenceSyncCalls mFenceSyncRegenCalls;
389 // Fence syncs to regen are a list of starting fence sync objects that were deleted and need to
390 // be regen'ed.
391 FenceSyncSet mFenceSyncsToRegen;
392 // Default uniforms that were modified during the run
393 DefaultUniformLocationsPerProgramMap mDefaultUniformsToReset;
394 // Calls per default uniform to return to original state
395 DefaultUniformCallsPerProgramMap mDefaultUniformResetCalls;
396 // Base location of arrayed uniforms
397 DefaultUniformBaseLocationMap mDefaultUniformBaseLocations;
398
399 // These data structures must be preserved across all captures
400
401 // Tracked resources per context
402 TrackedResourceArray mTrackedResourcesShared;
403 std::map<gl::ContextID, TrackedResourceArray> mTrackedResourcesPerContext;
404 std::map<EGLImage, egl::AttributeMap> mMatchImageToAttribs;
405 std::map<GLuint, egl::ImageID> mMatchTextureIDToImage;
406 std::map<gl::ShaderProgramID, ShaderProgramType> mShaderProgramType;
407 };
408
409 // CL specific resource tracker to track resource changes during the capture
410 #ifdef ANGLE_ENABLE_CL
411 struct ResourceTrackerCL final : angle::NonCopyable
412 {
413 ResourceTrackerCL();
414 ~ResourceTrackerCL();
415
416 // To obtain indices of CL arguments in replay
417 std::unordered_map<cl_platform_id, size_t> mCLPlatformIDIndices;
418 std::unordered_map<cl_device_id, size_t> mCLDeviceIDIndices;
419 std::unordered_map<cl_context, size_t> mCLContextIndices;
420 std::unordered_map<cl_event, size_t> mCLEventsIndices;
421 std::unordered_map<cl_command_queue, size_t> mCLCommandQueueIndices;
422 std::unordered_map<cl_mem, size_t> mCLMemIndices;
423 std::unordered_map<cl_sampler, size_t> mCLSamplerIndices;
424 std::unordered_map<cl_program, size_t> mCLProgramIndices;
425 std::unordered_map<cl_kernel, size_t> mCLKernelIndices;
426
427 std::unordered_map<const void *, size_t> mCLVoidIndices;
428
429 std::unordered_map<uint32_t, std::vector<size_t>> mCLParamIDToIndexVector;
430
431 // To account for cl mem or SVM pointers that are potentially dirty
432 // coming into the starting frame or from mapping and unmapping.
433 std::vector<cl_mem> mCLDirtyMem;
434 std::vector<void *> mCLDirtySVM;
435
436 // Keeps track of the # of times the program is linked, including it's own creation
437 std::unordered_map<cl_program, cl_uint> mCLProgramLinkCounter;
438
439 // To keep track of the sub buffer and parent replationship
440 std::unordered_map<cl_mem, cl_mem> mCLSubBufferToParent;
441
442 // To keep track of the linked programs
443 std::unordered_map<cl_program, std::vector<cl_program>> mCLLinkedPrograms;
444
445 // Program to all the kernels in the program
446 // So that when a program is released, it can also remove all the kernels.
447 std::unordered_map<cl_program, std::vector<cl_kernel>> mCLProgramToKernels;
448
449 // Kernel to program, to keep track of the program that a cloned kernel
450 // belongs to. Can't use ANGLE's getProgram() because the kernel object
451 // may be deleted by the time it's needed for capture.
452 std::unordered_map<cl_kernel, cl_program> mCLKernelToProgram;
453
454 // Mapped pointer to the map call
455 std::unordered_map<const void *, CallCapture> mCLMapCall;
456
457 // Gets the size of the SVM memory
458 std::unordered_map<const void *, size_t> SVMToSize;
459
460 cl_command_queue mCLCurrentCommandQueue;
461
462 std::vector<ParamCapture> mCLResetObjs;
463 };
464 #endif
465
466 // Used by the CPP replay to filter out unnecessary code.
467 using HasResourceTypeMap = angle::PackedEnumBitSet<ResourceIDType>;
468
469 // Map of ResourceType to IDs and range of setup calls
470 using ResourceIDToSetupCallsMap =
471 PackedEnumMap<ResourceIDType, std::map<GLuint, gl::Range<size_t>>>;
472
473 // Map of buffer ID to offset and size used when mapped
474 using BufferDataMap = std::map<gl::BufferID, std::pair<GLintptr, GLsizeiptr>>;
475
476 // A dictionary of sources indexed by shader type.
477 using ProgramSources = gl::ShaderMap<std::string>;
478
479 // Maps from IDs to sources.
480 using ShaderSourceMap = std::map<gl::ShaderProgramID, std::string>;
481 using ProgramSourceMap = std::map<gl::ShaderProgramID, ProgramSources>;
482
483 // Map from textureID to level and data
484 using TextureLevels = std::map<GLint, std::vector<uint8_t>>;
485 using TextureLevelDataMap = std::map<gl::TextureID, TextureLevels>;
486
487 struct SurfaceParams
488 {
489 gl::Extents extents;
490 egl::ColorSpace colorSpace;
491 };
492
493 // Map from ContextID to SurfaceParams
494 using SurfaceParamsMap = std::map<gl::ContextID, SurfaceParams>;
495
496 using CallVector = std::vector<std::vector<CallCapture> *>;
497
498 // A map from API entry point to calls
499 using CallResetMap = std::map<angle::EntryPoint, std::vector<CallCapture>>;
500
501 using TextureBinding = std::pair<size_t, gl::TextureType>;
502 using TextureResetMap = std::map<TextureBinding, gl::TextureID>;
503
504 using BufferBindingPair = std::pair<gl::BufferBinding, gl::BufferID>;
505
506 // StateResetHelper provides a simple way to track whether an entry point has been called during the
507 // trace, along with the reset calls to get it back to starting state. This is useful for things
508 // that are one dimensional, like context bindings or context state.
509 class StateResetHelper final : angle::NonCopyable
510 {
511 public:
512 StateResetHelper();
513 ~StateResetHelper();
514
getDirtyEntryPoints()515 const std::set<angle::EntryPoint> &getDirtyEntryPoints() const { return mDirtyEntryPoints; }
setEntryPointDirty(EntryPoint entryPoint)516 void setEntryPointDirty(EntryPoint entryPoint) { mDirtyEntryPoints.insert(entryPoint); }
517
getResetCalls()518 CallResetMap &getResetCalls() { return mResetCalls; }
getResetCalls()519 const CallResetMap &getResetCalls() const { return mResetCalls; }
520
521 void setDefaultResetCalls(const gl::Context *context, angle::EntryPoint);
522
getDirtyTextureBindings()523 const std::set<TextureBinding> &getDirtyTextureBindings() const
524 {
525 return mDirtyTextureBindings;
526 }
setTextureBindingDirty(size_t unit,gl::TextureType target)527 void setTextureBindingDirty(size_t unit, gl::TextureType target)
528 {
529 mDirtyTextureBindings.emplace(unit, target);
530 }
531
getResetTextureBindings()532 TextureResetMap &getResetTextureBindings() { return mResetTextureBindings; }
533
setResetActiveTexture(size_t textureID)534 void setResetActiveTexture(size_t textureID) { mResetActiveTexture = textureID; }
getResetActiveTexture()535 size_t getResetActiveTexture() { return mResetActiveTexture; }
536
getDirtyBufferBindings()537 const std::set<gl::BufferBinding> &getDirtyBufferBindings() const
538 {
539 return mDirtyBufferBindings;
540 }
setBufferBindingDirty(gl::BufferBinding binding)541 void setBufferBindingDirty(gl::BufferBinding binding) { mDirtyBufferBindings.insert(binding); }
542
getStartingBufferBindings()543 const std::set<BufferBindingPair> &getStartingBufferBindings() const
544 {
545 return mStartingBufferBindings;
546 }
setStartingBufferBinding(gl::BufferBinding binding,gl::BufferID bufferID)547 void setStartingBufferBinding(gl::BufferBinding binding, gl::BufferID bufferID)
548 {
549 mStartingBufferBindings.insert({binding, bufferID});
550 }
551
reset()552 void reset()
553 {
554 mDirtyEntryPoints.clear();
555 mResetCalls.clear();
556 mDirtyTextureBindings.clear();
557 mResetTextureBindings.clear();
558 mResetActiveTexture = 0;
559 mStartingBufferBindings.clear();
560 mDirtyBufferBindings.clear();
561 }
562
563 private:
564 // Dirty state per entry point
565 std::set<angle::EntryPoint> mDirtyEntryPoints;
566
567 // Reset calls per API entry point
568 CallResetMap mResetCalls;
569
570 // Dirty state per texture binding
571 std::set<TextureBinding> mDirtyTextureBindings;
572
573 // Texture bindings and active texture to restore
574 TextureResetMap mResetTextureBindings;
575 size_t mResetActiveTexture = 0;
576
577 // Starting and dirty buffer bindings
578 std::set<BufferBindingPair> mStartingBufferBindings;
579 std::set<gl::BufferBinding> mDirtyBufferBindings;
580 };
581
582 class FrameCapture final : angle::NonCopyable
583 {
584 public:
585 FrameCapture();
586 ~FrameCapture();
587
getSetupCalls()588 std::vector<CallCapture> &getSetupCalls() { return mSetupCalls; }
clearSetupCalls()589 void clearSetupCalls() { mSetupCalls.clear(); }
590
getStateResetHelper()591 StateResetHelper &getStateResetHelper() { return mStateResetHelper; }
592
593 void reset();
594
595 private:
596 std::vector<CallCapture> mSetupCalls;
597
598 StateResetHelper mStateResetHelper;
599 };
600
601 // Page range inside a coherent buffer
602 struct PageRange
603 {
604 PageRange(size_t start, size_t end);
605 ~PageRange();
606
607 // Relative start page
608 size_t start;
609
610 // First page after the relative end
611 size_t end;
612 };
613
614 // Memory address range defined by start and size
615 struct AddressRange
616 {
617 AddressRange();
618 AddressRange(uintptr_t start, size_t size);
619 ~AddressRange();
620
621 uintptr_t end();
622
623 uintptr_t start;
624 size_t size;
625 };
626
627 // Used to handle protection of buffers that overlap in pages.
628 enum class PageSharingType
629 {
630 NoneShared,
631 FirstShared,
632 LastShared,
633 FirstAndLastShared
634 };
635
636 class CoherentBuffer
637 {
638 public:
639 CoherentBuffer(uintptr_t start, size_t size, size_t pageSize, bool useShadowMemory);
640 ~CoherentBuffer();
641
642 // Sets the a range in the buffer clean and protects a selected range
643 void protectPageRange(const PageRange &pageRange);
644
645 // Sets all pages to clean and enables protection
646 void protectAll();
647
648 // Sets a page dirty state and sets it's protection
649 void setDirty(size_t relativePage, bool dirty);
650
651 // Shadow memory synchronization
652 void updateBufferMemory();
653 void updateShadowMemory();
654
655 // Removes protection
656 void removeProtection(PageSharingType sharingType);
657
658 bool contains(size_t page, size_t *relativePage);
659 bool isDirty();
660
661 // Returns dirty page ranges
662 std::vector<PageRange> getDirtyPageRanges();
663
664 // Calculates address range from page range
665 AddressRange getDirtyAddressRange(const PageRange &dirtyPageRange);
666 AddressRange getRange();
667
markShadowDirty()668 void markShadowDirty() { mShadowDirty = true; }
isShadowDirty()669 bool isShadowDirty() { return mShadowDirty; }
670
671 private:
672 // Actual buffer start and size
673 AddressRange mRange;
674
675 // Start and size of page aligned protected area
676 AddressRange mProtectionRange;
677
678 // Start and end of protection in relative pages, calculated from mProtectionRange.
679 size_t mProtectionStartPage;
680 size_t mProtectionEndPage;
681
682 size_t mPageCount;
683 size_t mPageSize;
684
685 // Clean pages are protected
686 std::vector<bool> mDirtyPages;
687
688 // shadow memory releated fields
689 bool mShadowMemoryEnabled;
690 uintptr_t mBufferStart;
691 void *mShadowMemory;
692 bool mShadowDirty;
693 };
694
695 class CoherentBufferTracker final : angle::NonCopyable
696 {
697 public:
698 CoherentBufferTracker();
699 ~CoherentBufferTracker();
700
701 bool isDirty(gl::BufferID id);
702 uintptr_t addBuffer(gl::BufferID id, uintptr_t start, size_t size);
703 void removeBuffer(gl::BufferID id);
704 void disable();
705 void enable();
706 void onEndFrame();
707 bool haveBuffer(gl::BufferID id);
isShadowMemoryEnabled()708 bool isShadowMemoryEnabled() { return mShadowMemoryEnabled; }
enableShadowMemory()709 void enableShadowMemory() { mShadowMemoryEnabled = true; }
710 void maybeUpdateShadowMemory();
711 void markAllShadowDirty();
712 // Determine whether memory protection can be used directly on graphics memory
713 bool canProtectDirectly(gl::Context *context);
714
715 private:
716 // Detect overlapping pages when removing protection
717 PageSharingType doesBufferSharePage(gl::BufferID id);
718
719 // Returns a map to found buffers and the corresponding pages for a given address.
720 // For addresses that are in a page shared by 2 buffers, 2 results are returned.
721 HashMap<std::shared_ptr<CoherentBuffer>, size_t> getBufferPagesForAddress(uintptr_t address);
722 PageFaultHandlerRangeType handleWrite(uintptr_t address);
723
724 public:
725 angle::SimpleMutex mMutex;
726 HashMap<GLuint, std::shared_ptr<CoherentBuffer>> mBuffers;
hasBeenReset()727 bool hasBeenReset() { return mHasBeenReset; }
728
729 private:
730 bool mEnabled;
731 bool mHasBeenReset;
732 std::unique_ptr<PageFaultHandler> mPageFaultHandler;
733 size_t mPageSize;
734
735 bool mShadowMemoryEnabled;
736 };
737
738 // Shared class for any items that need to be tracked by FrameCapture across shared contexts
739 class FrameCaptureShared final : angle::NonCopyable
740 {
741 public:
742 FrameCaptureShared();
743 ~FrameCaptureShared();
744
745 void captureCall(gl::Context *context, CallCapture &&call, bool isCallValid);
746 void checkForCaptureTrigger();
747 void onEndFrame(gl::Context *context);
748 void onDestroyContext(const gl::Context *context);
749 bool onEndCLCapture();
750 void onMakeCurrent(const gl::Context *context, const egl::Surface *drawSurface);
enabled()751 bool enabled() const { return mEnabled; }
752
753 bool isCapturing() const;
754 uint32_t getFrameCount() const;
755
756 // Returns a frame index starting from "1" as the first frame.
757 uint32_t getReplayFrameIndex() const;
758
759 #ifdef ANGLE_ENABLE_CL
760 void captureCLCall(CallCapture &&call, bool isCallValid);
761 static void onCLProgramEnd();
762
763 template <typename T>
getIndex(const T * object)764 size_t getIndex(const T *object)
765 {
766 if (getMap<T>().find(*object) == getMap<T>().end())
767 {
768 return SIZE_MAX;
769 }
770 return getMap<T>()[*object];
771 }
772 size_t getCLVoidIndex(const void *v);
773 std::vector<size_t> getCLObjVector(const angle::ParamCapture *paramCaptureKey);
774
775 template <typename T>
setIndex(const T * object)776 void setIndex(const T *object)
777 {
778 if (getMap<T>().find(*object) == getMap<T>().end())
779 {
780 size_t tempSize = getMap<T>().size();
781 getMap<T>()[*object] = tempSize;
782 }
783 }
784 void setCLPlatformIndices(cl_platform_id *platforms, size_t numPlatforms);
785 void setCLDeviceIndices(cl_device_id *devices, size_t numDevices);
786 void setCLVoidIndex(const void *v);
787 void setOffsetsVector(const void *args,
788 const void **argsLocations,
789 size_t numLocations,
790 const angle::ParamCapture *paramCaptureKey);
791 void setCLVoidVectorIndex(const void *pointers[],
792 size_t numPointers,
793 const angle::ParamCapture *paramCaptureKey);
794
795 template <typename T>
796 using MemberFuncPtr = size_t (FrameCaptureShared::*)(const T *);
797
798 template <typename T>
setCLObjVectorMap(const T * objs,size_t numObjs,const angle::ParamCapture * paramCaptureKey,MemberFuncPtr<const T> getCLObjIndexFunc)799 void setCLObjVectorMap(const T *objs,
800 size_t numObjs,
801 const angle::ParamCapture *paramCaptureKey,
802 MemberFuncPtr<const T> getCLObjIndexFunc)
803 {
804 mResourceTrackerCL.mCLParamIDToIndexVector[paramCaptureKey->uniqueID] =
805 std::vector<size_t>();
806 for (size_t i = 0; i < numObjs; ++i)
807 {
808 mResourceTrackerCL.mCLParamIDToIndexVector[paramCaptureKey->uniqueID].push_back(
809 (this->*getCLObjIndexFunc)(&objs[i]));
810 }
811 }
812
813 template <typename T>
814 std::unordered_map<T, size_t> &getMap();
815
816 void addCLResetObj(const angle::ParamCapture ¶m);
817 void removeCLResetObj(const ParamCapture ¶m);
818 void printCLResetObjs(std::stringstream &stream);
819
820 void trackCLMemUpdate(const cl_mem *mem, bool created);
821 void trackCLProgramUpdate(const cl_program *program,
822 bool referenced,
823 cl_uint numLinkedPrograms,
824 const cl_program *linkedPrograms);
825 void trackCLEvents(const cl_event *event, bool created);
826 void injectMemcpy(void *src, void *dest, size_t size, std::vector<CallCapture> *calls);
827 void captureUpdateCLObjs(std::vector<CallCapture> *calls);
828 void removeCLMemOccurrences(const cl_mem *mem, std::vector<CallCapture> *calls);
829 void removeCLKernelOccurrences(const cl_kernel *kernel, std::vector<CallCapture> *calls);
830 void removeCLProgramOccurrences(const cl_program *program, std::vector<CallCapture> *calls);
831 void removeCLCall(std::vector<CallCapture> *callVector, size_t &callIndex);
832 #endif
833
834 void trackBufferMapping(const gl::Context *context,
835 CallCapture *call,
836 gl::BufferID id,
837 gl::Buffer *buffer,
838 GLintptr offset,
839 GLsizeiptr length,
840 bool writable,
841 bool coherent);
842
843 void trackTextureUpdate(const gl::Context *context, const CallCapture &call);
844 void trackImageUpdate(const gl::Context *context, const CallCapture &call);
845 void trackDefaultUniformUpdate(const gl::Context *context, const CallCapture &call);
846 void trackVertexArrayUpdate(const gl::Context *context, const CallCapture &call);
847
848 const std::string &getShaderSource(gl::ShaderProgramID id) const;
849 void setShaderSource(gl::ShaderProgramID id, std::string sources);
850
851 const ProgramSources &getProgramSources(gl::ShaderProgramID id) const;
852 void setProgramSources(gl::ShaderProgramID id, ProgramSources sources);
853
854 // Load data from a previously stored texture level
855 const std::vector<uint8_t> &retrieveCachedTextureLevel(gl::TextureID id,
856 gl::TextureTarget target,
857 GLint level);
858
859 // Create new texture level data and copy the source into it
860 void copyCachedTextureLevel(const gl::Context *context,
861 gl::TextureID srcID,
862 GLint srcLevel,
863 gl::TextureID dstID,
864 GLint dstLevel,
865 const CallCapture &call);
866
867 // Create the location that should be used to cache texture level data
868 std::vector<uint8_t> &getCachedTextureLevelData(gl::Texture *texture,
869 gl::TextureTarget target,
870 GLint level,
871 EntryPoint entryPoint);
872
873 // Capture coherent buffer storages
874 void captureCoherentBufferSnapshot(const gl::Context *context, gl::BufferID bufferID);
875
876 // Remove any cached texture levels on deletion
877 void deleteCachedTextureLevelData(gl::TextureID id);
878
eraseBufferDataMapEntry(const gl::BufferID bufferId)879 void eraseBufferDataMapEntry(const gl::BufferID bufferId)
880 {
881 const auto &bufferDataInfo = mBufferDataMap.find(bufferId);
882 if (bufferDataInfo != mBufferDataMap.end())
883 {
884 mBufferDataMap.erase(bufferDataInfo);
885 }
886 }
887
hasBufferData(gl::BufferID bufferID)888 bool hasBufferData(gl::BufferID bufferID)
889 {
890 const auto &bufferDataInfo = mBufferDataMap.find(bufferID);
891 if (bufferDataInfo != mBufferDataMap.end())
892 {
893 return true;
894 }
895 return false;
896 }
897
getBufferDataOffsetAndLength(gl::BufferID bufferID)898 std::pair<GLintptr, GLsizeiptr> getBufferDataOffsetAndLength(gl::BufferID bufferID)
899 {
900 const auto &bufferDataInfo = mBufferDataMap.find(bufferID);
901 ASSERT(bufferDataInfo != mBufferDataMap.end());
902 return bufferDataInfo->second;
903 }
904
setCaptureActive()905 void setCaptureActive() { mCaptureActive = true; }
setCaptureInactive()906 void setCaptureInactive() { mCaptureActive = false; }
isCaptureActive()907 bool isCaptureActive() { return mCaptureActive; }
usesMidExecutionCapture()908 bool usesMidExecutionCapture() { return mCaptureStartFrame > 1; }
909
getWindowSurfaceContextID()910 gl::ContextID getWindowSurfaceContextID() const { return mWindowSurfaceContextID; }
911
912 void markResourceSetupCallsInactive(std::vector<CallCapture> *setupCalls,
913 ResourceIDType type,
914 GLuint id,
915 gl::Range<size_t> range);
916
917 void getOutputDirectory();
updateReadBufferSize(size_t readBufferSize)918 void updateReadBufferSize(size_t readBufferSize)
919 {
920 mReadBufferSize = std::max(mReadBufferSize, readBufferSize);
921 }
922
923 template <typename ResourceType>
handleGennedResource(const gl::Context * context,ResourceType resourceID)924 void handleGennedResource(const gl::Context *context, ResourceType resourceID)
925 {
926 if (isCaptureActive())
927 {
928 ResourceIDType idType = GetResourceIDTypeFromType<ResourceType>::IDType;
929 TrackedResource &tracker = mResourceTracker.getTrackedResource(context->id(), idType);
930 tracker.setGennedResource(resourceID.value);
931 }
932 }
933
934 template <typename ResourceType>
resourceIsGenerated(const gl::Context * context,ResourceType resourceID)935 bool resourceIsGenerated(const gl::Context *context, ResourceType resourceID)
936 {
937 ResourceIDType idType = GetResourceIDTypeFromType<ResourceType>::IDType;
938 TrackedResource &tracker = mResourceTracker.getTrackedResource(context->id(), idType);
939 return tracker.resourceIsGenerated(resourceID.value);
940 }
941
942 template <typename ResourceType>
handleDeletedResource(const gl::Context * context,ResourceType resourceID)943 void handleDeletedResource(const gl::Context *context, ResourceType resourceID)
944 {
945 if (isCaptureActive())
946 {
947 ResourceIDType idType = GetResourceIDTypeFromType<ResourceType>::IDType;
948 TrackedResource &tracker = mResourceTracker.getTrackedResource(context->id(), idType);
949 tracker.setDeletedResource(resourceID.value);
950 }
951 }
952
953 void *maybeGetShadowMemoryPointer(gl::Buffer *buffer, GLsizeiptr length, GLbitfield access);
954 void determineMemoryProtectionSupport(gl::Context *context);
955
getFrameCaptureMutex()956 angle::SimpleMutex &getFrameCaptureMutex() { return mFrameCaptureMutex; }
957
setDeferredLinkProgram(gl::ShaderProgramID programID)958 void setDeferredLinkProgram(gl::ShaderProgramID programID)
959 {
960 mDeferredLinkPrograms.emplace(programID);
961 }
isDeferredLinkProgram(gl::ShaderProgramID programID)962 bool isDeferredLinkProgram(gl::ShaderProgramID programID)
963 {
964 return (mDeferredLinkPrograms.find(programID) != mDeferredLinkPrograms.end());
965 }
966
967 static bool isRuntimeEnabled();
968
resetCaptureStartEndFrames()969 void resetCaptureStartEndFrames()
970 {
971 // If the trigger has been populated the frame range variables will be calculated
972 // based on the trigger value, so for now reset them to unreasonable values.
973 mCaptureStartFrame = mCaptureEndFrame = std::numeric_limits<uint32_t>::max();
974 INFO() << "Capture trigger detected, resetting capture start/end frame.";
975 }
976
977 private:
978 void writeJSON(const gl::Context *context);
979 void writeJSONCL();
980 void writeJSONCLGetInfo();
981 void saveCLGetInfo(const CallCapture &call);
982 void writeCppReplayIndexFiles(const gl::Context *context, bool writeResetContextCall);
983 void writeCppReplayIndexFilesCL();
984 void writeMainContextCppReplay(const gl::Context *context,
985 const std::vector<CallCapture> &setupCalls,
986 StateResetHelper &StateResetHelper);
987 void writeMainContextCppReplayCL();
988
989 void captureClientArraySnapshot(const gl::Context *context,
990 size_t vertexCount,
991 size_t instanceCount);
992 void captureMappedBufferSnapshot(const gl::Context *context, const CallCapture &call);
993
994 void copyCompressedTextureData(const gl::Context *context, const CallCapture &call);
995 void captureCompressedTextureData(const gl::Context *context, const CallCapture &call);
996
997 void reset();
998 void resetMidExecutionCapture(gl::Context *context);
999 void maybeOverrideEntryPoint(const gl::Context *context,
1000 CallCapture &call,
1001 std::vector<CallCapture> &newCalls);
1002 void maybeCapturePreCallUpdates(const gl::Context *context,
1003 CallCapture &call,
1004 std::vector<CallCapture> *shareGroupSetupCalls,
1005 ResourceIDToSetupCallsMap *resourceIDToSetupCalls);
1006 void maybeCapturePreCallUpdatesCL(CallCapture &call);
1007 template <typename ParamValueType>
1008 void maybeGenResourceOnBind(const gl::Context *context, CallCapture &call);
1009 void maybeCapturePostCallUpdates(const gl::Context *context);
1010 void maybeCapturePostCallUpdatesCL();
1011 void maybeCaptureDrawArraysClientData(const gl::Context *context,
1012 CallCapture &call,
1013 size_t instanceCount);
1014 void maybeCaptureDrawElementsClientData(const gl::Context *context,
1015 CallCapture &call,
1016 size_t instanceCount);
1017 void maybeCaptureCoherentBuffers(const gl::Context *context);
1018 void captureCustomMapBufferFromContext(const gl::Context *context,
1019 const char *entryPointName,
1020 CallCapture &call,
1021 std::vector<CallCapture> &callsOut);
1022 void updateCopyImageSubData(CallCapture &call);
1023 void overrideProgramBinary(const gl::Context *context,
1024 CallCapture &call,
1025 std::vector<CallCapture> &outCalls);
1026 void updateResourceCountsFromParamCapture(const ParamCapture ¶m, ResourceIDType idType);
1027 void updateResourceCountsFromParamCaptureCL(const ParamCapture ¶m, const CallCapture &call);
1028 void updateResourceCountsFromCallCapture(const CallCapture &call);
1029 void updateResourceCountsFromCallCaptureCL(const CallCapture &call);
1030
1031 void runMidExecutionCapture(gl::Context *context);
1032
1033 void scanSetupCalls(std::vector<CallCapture> &setupCalls);
1034
1035 std::vector<CallCapture> mFrameCalls;
1036
1037 // We save one large buffer of binary data for the whole CPP replay.
1038 // This simplifies a lot of file management.
1039 std::vector<uint8_t> mBinaryData;
1040
1041 bool mEnabled;
1042 static bool mRuntimeEnabled;
1043 static bool mRuntimeInitialized;
1044 bool mSerializeStateEnabled;
1045 std::string mOutDirectory;
1046 std::string mCaptureLabel;
1047 bool mCompression;
1048 gl::AttribArray<int> mClientVertexArrayMap;
1049 uint32_t mFrameIndex;
1050 uint32_t mCaptureStartFrame;
1051 uint32_t mCaptureEndFrame;
1052 bool mIsFirstFrame = true;
1053 bool mWroteIndexFile = false;
1054 SurfaceParamsMap mDrawSurfaceParams;
1055 gl::AttribArray<size_t> mClientArraySizes;
1056 size_t mReadBufferSize;
1057 size_t mResourceIDBufferSize;
1058 HasResourceTypeMap mHasResourceType;
1059 ResourceIDToSetupCallsMap mResourceIDToSetupCalls;
1060 BufferDataMap mBufferDataMap;
1061 bool mValidateSerializedState = false;
1062 std::string mValidationExpression;
1063 PackedEnumMap<ResourceIDType, uint32_t> mMaxAccessedResourceIDs;
1064 std::map<ParamType, uint32_t> mMaxCLParamsSize;
1065 CoherentBufferTracker mCoherentBufferTracker;
1066 angle::SimpleMutex mFrameCaptureMutex;
1067 bool mCallCaptured = false;
1068 bool mStartFrameCallCaptured = false;
1069
1070 // When true, it removes unnecessary calls going into
1071 // replay files that occur before mCaptureStartFrame
1072 bool removeUnneededOpenCLCalls = false;
1073
1074 #ifdef ANGLE_ENABLE_CL
1075 // OpenCL calls considered as "frames"
1076 std::unordered_set<EntryPoint> mCLEndFrameCalls = {EntryPoint::CLEnqueueNDRangeKernel,
1077 EntryPoint::CLEnqueueNativeKernel,
1078 EntryPoint::CLEnqueueTask};
1079
1080 // "Optional" OpenCL calls not important for Capture/Replay
1081 std::unordered_set<EntryPoint> mCLOptionalCalls = {EntryPoint::CLGetPlatformInfo,
1082 EntryPoint::CLGetDeviceInfo,
1083 EntryPoint::CLGetContextInfo,
1084 EntryPoint::CLGetCommandQueueInfo,
1085 EntryPoint::CLGetProgramInfo,
1086 EntryPoint::CLGetProgramBuildInfo,
1087 EntryPoint::CLGetKernelInfo,
1088 EntryPoint::CLGetKernelArgInfo,
1089 EntryPoint::CLGetKernelWorkGroupInfo,
1090 EntryPoint::CLGetEventInfo,
1091 EntryPoint::CLGetEventProfilingInfo,
1092 EntryPoint::CLGetMemObjectInfo,
1093 EntryPoint::CLGetImageInfo,
1094 EntryPoint::CLGetSamplerInfo,
1095 EntryPoint::CLGetSupportedImageFormats};
1096 std::string mCLInfoJson;
1097 std::vector<std::string> mExtFuncsAdded;
1098
1099 std::vector<CallCapture> mCLSetupCalls;
1100
1101 ResourceTrackerCL mResourceTrackerCL;
1102 #endif
1103
1104 ResourceTracker mResourceTracker;
1105 ReplayWriter mReplayWriter;
1106
1107 // If you don't know which frame you want to start capturing at, use the capture trigger.
1108 // Initialize it to the number of frames you want to capture, and then clear the value to 0 when
1109 // you reach the content you want to capture. Currently only available on Android.
1110 uint32_t mCaptureTrigger;
1111
1112 bool mCaptureActive;
1113 std::vector<uint32_t> mActiveFrameIndices;
1114
1115 // Cache most recently compiled and linked sources.
1116 ShaderSourceMap mCachedShaderSource;
1117 ProgramSourceMap mCachedProgramSources;
1118
1119 // Set of programs which were created but not linked before capture was started
1120 std::set<gl::ShaderProgramID> mDeferredLinkPrograms;
1121
1122 gl::ContextID mWindowSurfaceContextID;
1123
1124 std::vector<CallCapture> mShareGroupSetupCalls;
1125 // Track which Contexts were created and made current at least once before MEC,
1126 // requiring setup for replay
1127 std::unordered_set<GLuint> mActiveContexts;
1128
1129 // Invalid call counts per entry point while capture is active and inactive.
1130 std::unordered_map<EntryPoint, size_t> mInvalidCallCountsActive;
1131 std::unordered_map<EntryPoint, size_t> mInvalidCallCountsInactive;
1132 };
1133
1134 template <typename CaptureFuncT, typename... ArgsT>
CaptureGLCallToFrameCapture(CaptureFuncT captureFunc,bool isCallValid,gl::Context * context,ArgsT...captureParams)1135 void CaptureGLCallToFrameCapture(CaptureFuncT captureFunc,
1136 bool isCallValid,
1137 gl::Context *context,
1138 ArgsT... captureParams)
1139 {
1140 if (!FrameCaptureShared::isRuntimeEnabled())
1141 {
1142 // Return immediately to reduce overhead of compile-time flag
1143 return;
1144 }
1145 FrameCaptureShared *frameCaptureShared = context->getShareGroup()->getFrameCaptureShared();
1146
1147 // EGL calls are protected by the global context mutex but only a subset of GL calls
1148 // are so protected. Ensure FrameCaptureShared access thread safety by using a
1149 // frame-capture only mutex.
1150 std::lock_guard<angle::SimpleMutex> lock(frameCaptureShared->getFrameCaptureMutex());
1151
1152 if (!frameCaptureShared->isCapturing())
1153 {
1154 return;
1155 }
1156
1157 CallCapture call = captureFunc(context->getState(), isCallValid, captureParams...);
1158 frameCaptureShared->captureCall(context, std::move(call), isCallValid);
1159 }
1160
1161 template <typename FirstT, typename... OthersT>
GetEGLDisplayArg(FirstT display,OthersT...others)1162 egl::Display *GetEGLDisplayArg(FirstT display, OthersT... others)
1163 {
1164 if constexpr (std::is_same<egl::Display *, FirstT>::value)
1165 {
1166 return display;
1167 }
1168 return nullptr;
1169 }
1170
1171 template <typename CaptureFuncT, typename... ArgsT>
CaptureEGLCallToFrameCapture(CaptureFuncT captureFunc,bool isCallValid,egl::Thread * thread,ArgsT...captureParams)1172 void CaptureEGLCallToFrameCapture(CaptureFuncT captureFunc,
1173 bool isCallValid,
1174 egl::Thread *thread,
1175 ArgsT... captureParams)
1176 {
1177 if (!FrameCaptureShared::isRuntimeEnabled())
1178 {
1179 // Return immediately to reduce overhead of compile-time flag
1180 return;
1181 }
1182 gl::Context *context = thread->getContext();
1183 if (!context)
1184 {
1185 // Get a valid context from the display argument if no context is associated with this
1186 // thread
1187 egl::Display *display = GetEGLDisplayArg(captureParams...);
1188 if (display)
1189 {
1190 for (const auto &contextIter : display->getState().contextMap)
1191 {
1192 context = contextIter.second;
1193 break;
1194 }
1195 }
1196 if (!context)
1197 {
1198 return;
1199 }
1200 }
1201 std::lock_guard<egl::ContextMutex> lock(context->getContextMutex());
1202
1203 angle::FrameCaptureShared *frameCaptureShared =
1204 context->getShareGroup()->getFrameCaptureShared();
1205 if (!frameCaptureShared->isCapturing())
1206 {
1207 return;
1208 }
1209
1210 angle::CallCapture call = captureFunc(thread, isCallValid, captureParams...);
1211 frameCaptureShared->captureCall(context, std::move(call), true);
1212 }
1213
1214 #ifdef ANGLE_ENABLE_CL
1215 template <typename CaptureFuncT, typename... ArgsT>
CaptureCLCallToFrameCapture(CaptureFuncT captureFunc,bool isCallValid,ArgsT...captureParams)1216 void CaptureCLCallToFrameCapture(CaptureFuncT captureFunc, bool isCallValid, ArgsT... captureParams)
1217 {
1218 if (!FrameCaptureShared::isRuntimeEnabled())
1219 {
1220 // Return immediately to reduce overhead of compile-time flag
1221 return;
1222 }
1223 angle::FrameCaptureShared *frameCaptureShared =
1224 cl::Platform::GetDefault()->getFrameCaptureShared();
1225 std::lock_guard<angle::SimpleMutex> lock(frameCaptureShared->getFrameCaptureMutex());
1226 if (!frameCaptureShared || !frameCaptureShared->isCapturing())
1227 {
1228 return;
1229 }
1230 angle::CallCapture call = captureFunc(isCallValid, captureParams...);
1231 frameCaptureShared->captureCLCall(std::move(call), isCallValid);
1232 }
1233 #endif
1234
1235 // Pointer capture helpers.
1236 void CaptureMemory(const void *source, size_t size, ParamCapture *paramCapture);
1237 void CaptureString(const GLchar *str, ParamCapture *paramCapture);
1238 void CaptureStringLimit(const GLchar *str, uint32_t limit, ParamCapture *paramCapture);
1239 void CaptureVertexPointerGLES1(const gl::State &glState,
1240 gl::ClientVertexArrayType type,
1241 const void *pointer,
1242 ParamCapture *paramCapture);
1243
1244 gl::Program *GetProgramForCapture(const gl::State &glState, gl::ShaderProgramID handle);
1245
1246 // For GetIntegerv, GetFloatv, etc.
1247 void CaptureGetParameter(const gl::State &glState,
1248 GLenum pname,
1249 size_t typeSize,
1250 ParamCapture *paramCapture);
1251
1252 void CaptureGetActiveUniformBlockivParameters(const gl::State &glState,
1253 gl::ShaderProgramID handle,
1254 gl::UniformBlockIndex uniformBlockIndex,
1255 GLenum pname,
1256 ParamCapture *paramCapture);
1257
1258 template <typename T>
CaptureClearBufferValue(GLenum buffer,const T * value,ParamCapture * paramCapture)1259 void CaptureClearBufferValue(GLenum buffer, const T *value, ParamCapture *paramCapture)
1260 {
1261 // Per the spec, color buffers have a vec4, the rest a single value
1262 uint32_t valueSize = (buffer == GL_COLOR) ? 4 : 1;
1263 CaptureMemory(value, valueSize * sizeof(T), paramCapture);
1264 }
1265
1266 void CaptureGenHandlesImpl(GLsizei n, GLuint *handles, ParamCapture *paramCapture);
1267
1268 template <typename T>
CaptureGenHandles(GLsizei n,T * handles,ParamCapture * paramCapture)1269 void CaptureGenHandles(GLsizei n, T *handles, ParamCapture *paramCapture)
1270 {
1271 paramCapture->dataNElements = n;
1272 CaptureGenHandlesImpl(n, reinterpret_cast<GLuint *>(handles), paramCapture);
1273 }
1274
1275 template <typename T>
CaptureArray(T * elements,GLsizei n,ParamCapture * paramCapture)1276 void CaptureArray(T *elements, GLsizei n, ParamCapture *paramCapture)
1277 {
1278 paramCapture->dataNElements = n;
1279 CaptureMemory(elements, n * sizeof(T), paramCapture);
1280 }
1281
1282 void CaptureShaderStrings(GLsizei count,
1283 const GLchar *const *strings,
1284 const GLint *length,
1285 ParamCapture *paramCapture);
1286
1287 bool IsTrackedPerContext(ResourceIDType type);
1288
1289 // Function declarations & data types for both
1290 // capturing OpenGL and OpenCL
1291
1292 std::string EscapeString(const std::string &string);
1293
1294 // Used to indicate that "shared" should be used to identify the files.
1295 constexpr gl::ContextID kSharedContextId = {0};
1296 // Used to indicate no context ID should be output.
1297 constexpr gl::ContextID kNoContextId = {std::numeric_limits<uint32_t>::max()};
1298
1299 constexpr uint32_t kNoPartId = std::numeric_limits<uint32_t>::max();
1300
1301 std::ostream &operator<<(std::ostream &os, gl::ContextID contextId);
1302
1303 struct FmtCapturePrefix
1304 {
FmtCapturePrefixFmtCapturePrefix1305 FmtCapturePrefix(gl::ContextID contextIdIn, const std::string &captureLabelIn)
1306 : contextId(contextIdIn), captureLabel(captureLabelIn)
1307 {}
1308 gl::ContextID contextId;
1309 const std::string &captureLabel;
1310 };
1311
1312 std::ostream &operator<<(std::ostream &os, const FmtCapturePrefix &fmt);
1313
1314 // In C, when you declare or define a function that takes no parameters, you must explicitly say the
1315 // function takes "void" parameters. When you're calling the function you omit this void. It's
1316 // therefore necessary to know how we're using a function to know if we should emi the "void".
1317 enum FuncUsage
1318 {
1319 Prototype,
1320 Definition,
1321 Call,
1322 };
1323
1324 std::ostream &operator<<(std::ostream &os, FuncUsage usage);
1325
1326 struct FmtReplayFunction
1327 {
1328 FmtReplayFunction(gl::ContextID contextIdIn,
1329 FuncUsage usageIn,
1330 uint32_t frameIndexIn,
1331 uint32_t partIdIn = kNoPartId)
contextIdFmtReplayFunction1332 : contextId(contextIdIn), usage(usageIn), frameIndex(frameIndexIn), partId(partIdIn)
1333 {}
1334 gl::ContextID contextId;
1335 FuncUsage usage;
1336 uint32_t frameIndex;
1337 uint32_t partId;
1338 };
1339
1340 std::ostream &operator<<(std::ostream &os, const FmtReplayFunction &fmt);
1341
1342 enum class ReplayFunc
1343 {
1344 Replay,
1345 Setup,
1346 SetupInactive,
1347 Reset,
1348 SetupFirstFrame,
1349 };
1350
1351 struct FmtFunction
1352 {
FmtFunctionFmtFunction1353 FmtFunction(ReplayFunc funcTypeIn,
1354 gl::ContextID contextIdIn,
1355 FuncUsage usageIn,
1356 uint32_t frameIndexIn,
1357 uint32_t partIdIn)
1358 : funcType(funcTypeIn),
1359 contextId(contextIdIn),
1360 usage(usageIn),
1361 frameIndex(frameIndexIn),
1362 partId(partIdIn)
1363 {}
1364
1365 ReplayFunc funcType;
1366 gl::ContextID contextId;
1367 FuncUsage usage;
1368 uint32_t frameIndex;
1369 uint32_t partId;
1370 };
1371
1372 std::ostream &operator<<(std::ostream &os, gl::ContextID contextId);
1373
1374 std::ostream &operator<<(std::ostream &os, const FmtFunction &fmt);
1375
1376 struct FmtSetupFunction
1377 {
FmtSetupFunctionFmtSetupFunction1378 FmtSetupFunction(uint32_t partIdIn, gl::ContextID contextIdIn, FuncUsage usageIn)
1379 : partId(partIdIn), contextId(contextIdIn), usage(usageIn)
1380 {}
1381
1382 uint32_t partId;
1383 gl::ContextID contextId;
1384 FuncUsage usage;
1385 };
1386
1387 std::ostream &operator<<(std::ostream &os, const FmtSetupFunction &fmt);
1388
1389 struct FmtSetupFirstFrameFunction
1390 {
partIdFmtSetupFirstFrameFunction1391 FmtSetupFirstFrameFunction(uint32_t partIdIn = kNoPartId) : partId(partIdIn) {}
1392
1393 uint32_t partId;
1394 };
1395
1396 std::ostream &operator<<(std::ostream &os, const FmtSetupFirstFrameFunction &fmt);
1397
1398 struct FmtSetupInactiveFunction
1399 {
FmtSetupInactiveFunctionFmtSetupInactiveFunction1400 FmtSetupInactiveFunction(uint32_t partIdIn, gl::ContextID contextIdIn, FuncUsage usageIn)
1401 : partId(partIdIn), contextId(contextIdIn), usage(usageIn)
1402 {}
1403
1404 uint32_t partId;
1405 gl::ContextID contextId;
1406 FuncUsage usage;
1407 };
1408
1409 std::ostream &operator<<(std::ostream &os, const FmtSetupInactiveFunction &fmt);
1410
1411 struct FmtResetFunction
1412 {
FmtResetFunctionFmtResetFunction1413 FmtResetFunction(uint32_t partIdIn, gl::ContextID contextIdIn, FuncUsage usageIn)
1414 : partId(partIdIn), contextId(contextIdIn), usage(usageIn)
1415 {}
1416
1417 uint32_t partId;
1418 gl::ContextID contextId;
1419 FuncUsage usage;
1420 };
1421
1422 std::ostream &operator<<(std::ostream &os, const FmtResetFunction &fmt);
1423
1424 // For compatibility with C, which does not have multi-line string literals, we break strings up
1425 // into multiple lines like:
1426 //
1427 // const char *str[] = {
1428 // "multiple\n"
1429 // "line\n"
1430 // "strings may have \"quotes\"\n"
1431 // "and \\slashes\\\n",
1432 // };
1433 //
1434 // Note we need to emit extra escapes to ensure quotes and other special characters are preserved.
1435 struct FmtMultiLineString
1436 {
FmtMultiLineStringFmtMultiLineString1437 FmtMultiLineString(const std::string &str) : strings()
1438 {
1439 std::string str2;
1440
1441 // Strip any carriage returns before splitting, for consistency
1442 if (str.find("\r") != std::string::npos)
1443 {
1444 // str is const, so have to make a copy of it first
1445 str2 = str;
1446 ReplaceAllSubstrings(&str2, "\r", "");
1447 }
1448
1449 strings =
1450 angle::SplitString(str2.empty() ? str : str2, "\n", WhitespaceHandling::KEEP_WHITESPACE,
1451 SplitResult::SPLIT_WANT_ALL);
1452 }
1453
1454 std::vector<std::string> strings;
1455 };
1456
1457 std::ostream &operator<<(std::ostream &ostr, const FmtMultiLineString &fmt);
1458
1459 struct SaveFileHelper
1460 {
1461 public:
1462 // We always use ios::binary to avoid inconsistent line endings when captured on Linux vs Win.
SaveFileHelperSaveFileHelper1463 SaveFileHelper(const std::string &filePathIn)
1464 : mOfs(filePathIn, std::ios::binary | std::ios::out), mFilePath(filePathIn)
1465 {
1466 if (!mOfs.is_open())
1467 {
1468 FATAL() << "Could not open " << filePathIn;
1469 }
1470 }
~SaveFileHelperSaveFileHelper1471 ~SaveFileHelper() { printf("Saved '%s'.\n", mFilePath.c_str()); }
1472
1473 template <typename T>
1474 SaveFileHelper &operator<<(const T &value)
1475 {
1476 mOfs << value;
1477 if (mOfs.bad())
1478 {
1479 FATAL() << "Error writing to " << mFilePath;
1480 }
1481 return *this;
1482 }
1483
writeSaveFileHelper1484 void write(const uint8_t *data, size_t size)
1485 {
1486 mOfs.write(reinterpret_cast<const char *>(data), size);
1487 }
1488
1489 private:
1490 void checkError();
1491
1492 std::ofstream mOfs;
1493 std::string mFilePath;
1494 };
1495
1496 // TODO: Consolidate to C output and remove option. http://anglebug.com/42266223
1497
1498 constexpr char kEnabledVarName[] = "ANGLE_CAPTURE_ENABLED";
1499 constexpr char kOutDirectoryVarName[] = "ANGLE_CAPTURE_OUT_DIR";
1500 constexpr char kFrameStartVarName[] = "ANGLE_CAPTURE_FRAME_START";
1501 constexpr char kFrameEndVarName[] = "ANGLE_CAPTURE_FRAME_END";
1502 constexpr char kTriggerVarName[] = "ANGLE_CAPTURE_TRIGGER";
1503 constexpr char kCaptureLabelVarName[] = "ANGLE_CAPTURE_LABEL";
1504 constexpr char kCompressionVarName[] = "ANGLE_CAPTURE_COMPRESSION";
1505 constexpr char kSerializeStateVarName[] = "ANGLE_CAPTURE_SERIALIZE_STATE";
1506 constexpr char kValidationVarName[] = "ANGLE_CAPTURE_VALIDATION";
1507 constexpr char kValidationExprVarName[] = "ANGLE_CAPTURE_VALIDATION_EXPR";
1508 constexpr char kSourceExtVarName[] = "ANGLE_CAPTURE_SOURCE_EXT";
1509 constexpr char kSourceSizeVarName[] = "ANGLE_CAPTURE_SOURCE_SIZE";
1510 constexpr char kForceShadowVarName[] = "ANGLE_CAPTURE_FORCE_SHADOW";
1511
1512 constexpr size_t kBinaryAlignment = 16;
1513 constexpr size_t kFunctionSizeLimit = 5000;
1514
1515 // Limit based on MSVC Compiler Error C2026
1516 constexpr size_t kStringLengthLimit = 16380;
1517
1518 // Default limit to number of bytes in a capture source files.
1519 constexpr char kDefaultSourceFileExt[] = "cpp";
1520 constexpr size_t kDefaultSourceFileSizeThreshold = 400000;
1521
1522 // Android debug properties that correspond to the above environment variables
1523 constexpr char kAndroidEnabled[] = "debug.angle.capture.enabled";
1524 constexpr char kAndroidOutDir[] = "debug.angle.capture.out_dir";
1525 constexpr char kAndroidFrameStart[] = "debug.angle.capture.frame_start";
1526 constexpr char kAndroidFrameEnd[] = "debug.angle.capture.frame_end";
1527 constexpr char kAndroidTrigger[] = "debug.angle.capture.trigger";
1528 constexpr char kAndroidCaptureLabel[] = "debug.angle.capture.label";
1529 constexpr char kAndroidCompression[] = "debug.angle.capture.compression";
1530 constexpr char kAndroidValidation[] = "debug.angle.capture.validation";
1531 constexpr char kAndroidValidationExpr[] = "debug.angle.capture.validation_expr";
1532 constexpr char kAndroidSourceExt[] = "debug.angle.capture.source_ext";
1533 constexpr char kAndroidSourceSize[] = "debug.angle.capture.source_size";
1534 constexpr char kAndroidForceShadow[] = "debug.angle.capture.force_shadow";
1535
1536 void WriteCppReplayForCall(const CallCapture &call,
1537 ReplayWriter &replayWriter,
1538 std::ostream &out,
1539 std::ostream &header,
1540 std::vector<uint8_t> *binaryData,
1541 size_t *maxResourceIDBufferSize);
1542
1543 void WriteCppReplayForCallCL(const CallCapture &call,
1544 ReplayWriter &replayWriter,
1545 std::ostream &out,
1546 std::ostream &header,
1547 std::vector<uint8_t> *binaryData);
1548
1549 void WriteBinaryParamReplay(ReplayWriter &replayWriter,
1550 std::ostream &out,
1551 std::ostream &header,
1552 const CallCapture &call,
1553 const ParamCapture ¶m,
1554 std::vector<uint8_t> *binaryData);
1555
1556 std::string GetBinaryDataFilePath(bool compression, const std::string &captureLabel);
1557
1558 void SaveBinaryData(bool compression,
1559 const std::string &outDir,
1560 gl::ContextID contextId,
1561 const std::string &captureLabel,
1562 const std::vector<uint8_t> &binaryData);
1563
1564 void WriteStringPointerParamReplay(ReplayWriter &replayWriter,
1565 std::ostream &out,
1566 std::ostream &header,
1567 const CallCapture &call,
1568 const ParamCapture ¶m);
1569
1570 void WriteCppReplayFunctionWithParts(const gl::ContextID contextID,
1571 ReplayFunc replayFunc,
1572 ReplayWriter &replayWriter,
1573 uint32_t frameIndex,
1574 std::vector<uint8_t> *binaryData,
1575 const std::vector<CallCapture> &calls,
1576 std::stringstream &header,
1577 std::stringstream &out,
1578 size_t *maxResourceIDBufferSize);
1579
1580 void WriteComment(std::ostream &out, const CallCapture &call);
1581
1582 template <typename T, typename CastT = T>
WriteInlineData(const std::vector<uint8_t> & vec,std::ostream & out)1583 void WriteInlineData(const std::vector<uint8_t> &vec, std::ostream &out)
1584 {
1585 const T *data = reinterpret_cast<const T *>(vec.data());
1586 size_t count = vec.size() / sizeof(T);
1587
1588 if (data == nullptr)
1589 {
1590 return;
1591 }
1592
1593 out << static_cast<CastT>(data[0]);
1594
1595 for (size_t dataIndex = 1; dataIndex < count; ++dataIndex)
1596 {
1597 out << ", " << static_cast<CastT>(data[dataIndex]);
1598 }
1599 }
1600
1601 template <>
1602 void WriteInlineData<GLchar>(const std::vector<uint8_t> &vec, std::ostream &out);
1603
1604 void AddComment(std::vector<CallCapture> *outCalls, const std::string &comment);
1605
1606 } // namespace angle
1607
1608 template <typename T>
CaptureTextureAndSamplerParameter_params(GLenum pname,const T * param,angle::ParamCapture * paramCapture)1609 void CaptureTextureAndSamplerParameter_params(GLenum pname,
1610 const T *param,
1611 angle::ParamCapture *paramCapture)
1612 {
1613 if (pname == GL_TEXTURE_BORDER_COLOR || pname == GL_TEXTURE_CROP_RECT_OES)
1614 {
1615 CaptureMemory(param, sizeof(T) * 4, paramCapture);
1616 }
1617 else
1618 {
1619 CaptureMemory(param, sizeof(T), paramCapture);
1620 }
1621 }
1622
1623 namespace egl
1624 {
1625 angle::ParamCapture CaptureAttributeMap(const egl::AttributeMap &attribMap);
1626 } // namespace egl
1627
1628 #endif // LIBANGLE_FRAME_CAPTURE_H_
1629