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 "common/PackedEnums.h"
14 #include "common/SimpleMutex.h"
15 #include "common/frame_capture_utils.h"
16 #include "common/system_utils.h"
17 #include "libANGLE/Context.h"
18 #include "libANGLE/ShareGroup.h"
19 #include "libANGLE/Thread.h"
20 #include "libANGLE/angletypes.h"
21 #include "libANGLE/entry_points_utils.h"
22
23 namespace gl
24 {
25 enum class BigGLEnum;
26 enum class GLESEnum;
27 } // namespace gl
28
29 namespace angle
30 {
31 // Helper to use unique IDs for each local data variable.
32 class DataCounters final : angle::NonCopyable
33 {
34 public:
35 DataCounters();
36 ~DataCounters();
37
38 int getAndIncrement(EntryPoint entryPoint, const std::string ¶mName);
39
40 private:
41 // <CallName, ParamName>
42 using Counter = std::pair<EntryPoint, std::string>;
43 std::map<Counter, int> mData;
44 };
45
46 constexpr int kStringsNotFound = -1;
47 class StringCounters final : angle::NonCopyable
48 {
49 public:
50 StringCounters();
51 ~StringCounters();
52
53 int getStringCounter(const std::vector<std::string> &str);
54 void setStringCounter(const std::vector<std::string> &str, int &counter);
55
56 private:
57 std::map<std::vector<std::string>, int> mStringCounterMap;
58 };
59
60 class DataTracker final : angle::NonCopyable
61 {
62 public:
63 DataTracker();
64 ~DataTracker();
65
getCounters()66 DataCounters &getCounters() { return mCounters; }
getStringCounters()67 StringCounters &getStringCounters() { return mStringCounters; }
68
69 private:
70 DataCounters mCounters;
71 StringCounters mStringCounters;
72 };
73
74 class ReplayWriter final : angle::NonCopyable
75 {
76 public:
77 ReplayWriter();
78 ~ReplayWriter();
79
80 void setSourceFileExtension(const char *ext);
81 void setSourceFileSizeThreshold(size_t sourceFileSizeThreshold);
82 void setFilenamePattern(const std::string &pattern);
83 void setCaptureLabel(const std::string &label);
84 void setSourcePrologue(const std::string &prologue);
85 void setHeaderPrologue(const std::string &prologue);
86
87 void addPublicFunction(const std::string &functionProto,
88 const std::stringstream &headerStream,
89 const std::stringstream &bodyStream);
90 void addPrivateFunction(const std::string &functionProto,
91 const std::stringstream &headerStream,
92 const std::stringstream &bodyStream);
93 std::string getInlineVariableName(EntryPoint entryPoint, const std::string ¶mName);
94
95 std::string getInlineStringSetVariableName(EntryPoint entryPoint,
96 const std::string ¶mName,
97 const std::vector<std::string> &strings,
98 bool *isNewEntryOut);
99
100 void saveFrame();
101 void saveFrameIfFull();
102 void saveIndexFilesAndHeader();
103 void saveSetupFile();
104
105 std::vector<std::string> getAndResetWrittenFiles();
106
107 private:
108 static std::string GetVarName(EntryPoint entryPoint, const std::string ¶mName, int counter);
109
110 void saveHeader();
111 void writeReplaySource(const std::string &filename);
112 void addWrittenFile(const std::string &filename);
113 size_t getStoredReplaySourceSize() const;
114
115 std::string mSourceFileExtension;
116 size_t mSourceFileSizeThreshold;
117 size_t mFrameIndex;
118
119 DataTracker mDataTracker;
120 std::string mFilenamePattern;
121 std::string mCaptureLabel;
122 std::string mSourcePrologue;
123 std::string mHeaderPrologue;
124
125 std::vector<std::string> mReplayHeaders;
126 std::vector<std::string> mGlobalVariableDeclarations;
127
128 std::vector<std::string> mPublicFunctionPrototypes;
129 std::vector<std::string> mPublicFunctions;
130
131 std::vector<std::string> mPrivateFunctionPrototypes;
132 std::vector<std::string> mPrivateFunctions;
133
134 std::vector<std::string> mWrittenFiles;
135 };
136
137 using BufferCalls = std::map<GLuint, std::vector<CallCapture>>;
138
139 // true means mapped, false means unmapped
140 using BufferMapStatusMap = std::map<GLuint, bool>;
141
142 using FenceSyncSet = std::set<gl::SyncID>;
143 using FenceSyncCalls = std::map<gl::SyncID, std::vector<CallCapture>>;
144
145 // For default uniforms, we need to track which ones are dirty, and the series of calls to reset.
146 // Each program has unique default uniforms, and each uniform has one or more locations in the
147 // default buffer. For reset efficiency, we track only the uniforms dirty by location, per program.
148
149 // A set of all default uniforms (per program) that were modified during the run
150 using DefaultUniformLocationsSet = std::set<gl::UniformLocation>;
151 using DefaultUniformLocationsPerProgramMap =
152 std::map<gl::ShaderProgramID, DefaultUniformLocationsSet>;
153
154 // A map of programs which maps to locations and their reset calls
155 using DefaultUniformCallsPerLocationMap = std::map<gl::UniformLocation, std::vector<CallCapture>>;
156 using DefaultUniformCallsPerProgramMap =
157 std::map<gl::ShaderProgramID, DefaultUniformCallsPerLocationMap>;
158
159 using DefaultUniformBaseLocationMap =
160 std::map<std::pair<gl::ShaderProgramID, gl::UniformLocation>, gl::UniformLocation>;
161
162 using ResourceSet = std::set<GLuint>;
163 using ResourceCalls = std::map<GLuint, std::vector<CallCapture>>;
164
165 class TrackedResource final : angle::NonCopyable
166 {
167 public:
168 TrackedResource();
169 ~TrackedResource();
170
getStartingResources()171 const ResourceSet &getStartingResources() const { return mStartingResources; }
getStartingResources()172 ResourceSet &getStartingResources() { return mStartingResources; }
getNewResources()173 const ResourceSet &getNewResources() const { return mNewResources; }
getNewResources()174 ResourceSet &getNewResources() { return mNewResources; }
getResourcesToDelete()175 const ResourceSet &getResourcesToDelete() const { return mResourcesToDelete; }
getResourcesToDelete()176 ResourceSet &getResourcesToDelete() { return mResourcesToDelete; }
getResourcesToRegen()177 const ResourceSet &getResourcesToRegen() const { return mResourcesToRegen; }
getResourcesToRegen()178 ResourceSet &getResourcesToRegen() { return mResourcesToRegen; }
getResourcesToRestore()179 const ResourceSet &getResourcesToRestore() const { return mResourcesToRestore; }
getResourcesToRestore()180 ResourceSet &getResourcesToRestore() { return mResourcesToRestore; }
181
182 void setGennedResource(GLuint id);
183 void setDeletedResource(GLuint id);
184 void setModifiedResource(GLuint id);
185 bool resourceIsGenerated(GLuint id);
186
getResourceRegenCalls()187 ResourceCalls &getResourceRegenCalls() { return mResourceRegenCalls; }
getResourceRestoreCalls()188 ResourceCalls &getResourceRestoreCalls() { return mResourceRestoreCalls; }
189
190 private:
191 // Resource regen calls will gen a resource
192 ResourceCalls mResourceRegenCalls;
193 // Resource restore calls will restore the contents of a resource
194 ResourceCalls mResourceRestoreCalls;
195
196 // Resources created during startup
197 ResourceSet mStartingResources;
198
199 // Resources created during the run that need to be deleted
200 ResourceSet mNewResources;
201 // Resources recreated during the run that need to be deleted
202 ResourceSet mResourcesToDelete;
203 // Resources deleted during the run that need to be recreated
204 ResourceSet mResourcesToRegen;
205 // Resources modified during the run that need to be restored
206 ResourceSet mResourcesToRestore;
207 };
208
209 using TrackedResourceArray =
210 std::array<TrackedResource, static_cast<uint32_t>(ResourceIDType::EnumCount)>;
211
212 enum class ShaderProgramType
213 {
214 ShaderType,
215 ProgramType
216 };
217
218 // Helper to track resource changes during the capture
219 class ResourceTracker final : angle::NonCopyable
220 {
221 public:
222 ResourceTracker();
223 ~ResourceTracker();
224
getBufferMapCalls()225 BufferCalls &getBufferMapCalls() { return mBufferMapCalls; }
getBufferUnmapCalls()226 BufferCalls &getBufferUnmapCalls() { return mBufferUnmapCalls; }
227
getBufferBindingCalls()228 std::vector<CallCapture> &getBufferBindingCalls() { return mBufferBindingCalls; }
229
230 void setBufferMapped(gl::ContextID contextID, GLuint id);
231 void setBufferUnmapped(gl::ContextID contextID, GLuint id);
232
233 bool getStartingBuffersMappedCurrent(GLuint id) const;
234 bool getStartingBuffersMappedInitial(GLuint id) const;
235
setStartingBufferMapped(GLuint id,bool mapped)236 void setStartingBufferMapped(GLuint id, bool mapped)
237 {
238 // Track the current state (which will change throughout the trace)
239 mStartingBuffersMappedCurrent[id] = mapped;
240
241 // And the initial state, to compare during frame loop reset
242 mStartingBuffersMappedInitial[id] = mapped;
243 }
244
245 void onShaderProgramAccess(gl::ShaderProgramID shaderProgramID);
getMaxShaderPrograms()246 uint32_t getMaxShaderPrograms() const { return mMaxShaderPrograms; }
247
getStartingFenceSyncs()248 FenceSyncSet &getStartingFenceSyncs() { return mStartingFenceSyncs; }
getFenceSyncRegenCalls()249 FenceSyncCalls &getFenceSyncRegenCalls() { return mFenceSyncRegenCalls; }
getFenceSyncsToRegen()250 FenceSyncSet &getFenceSyncsToRegen() { return mFenceSyncsToRegen; }
251 void setDeletedFenceSync(gl::SyncID sync);
252
getDefaultUniformsToReset()253 DefaultUniformLocationsPerProgramMap &getDefaultUniformsToReset()
254 {
255 return mDefaultUniformsToReset;
256 }
getDefaultUniformResetCalls(gl::ShaderProgramID id)257 DefaultUniformCallsPerLocationMap &getDefaultUniformResetCalls(gl::ShaderProgramID id)
258 {
259 return mDefaultUniformResetCalls[id];
260 }
261 void setModifiedDefaultUniform(gl::ShaderProgramID programID, gl::UniformLocation location);
262 void setDefaultUniformBaseLocation(gl::ShaderProgramID programID,
263 gl::UniformLocation location,
264 gl::UniformLocation baseLocation);
getDefaultUniformBaseLocation(gl::ShaderProgramID programID,gl::UniformLocation location)265 gl::UniformLocation getDefaultUniformBaseLocation(gl::ShaderProgramID programID,
266 gl::UniformLocation location)
267 {
268 ASSERT(mDefaultUniformBaseLocations.find({programID, location}) !=
269 mDefaultUniformBaseLocations.end());
270 return mDefaultUniformBaseLocations[{programID, location}];
271 }
272
273 TrackedResource &getTrackedResource(gl::ContextID contextID, ResourceIDType type);
274
275 void getContextIDs(std::set<gl::ContextID> &idsOut);
276
getImageToAttribTable()277 std::map<EGLImage, egl::AttributeMap> &getImageToAttribTable() { return mMatchImageToAttribs; }
278
getTextureIDToImageTable()279 std::map<GLuint, egl::ImageID> &getTextureIDToImageTable() { return mMatchTextureIDToImage; }
280
setShaderProgramType(gl::ShaderProgramID id,angle::ShaderProgramType type)281 void setShaderProgramType(gl::ShaderProgramID id, angle::ShaderProgramType type)
282 {
283 mShaderProgramType[id] = type;
284 }
getShaderProgramType(gl::ShaderProgramID id)285 ShaderProgramType getShaderProgramType(gl::ShaderProgramID id)
286 {
287 ASSERT(mShaderProgramType.find(id) != mShaderProgramType.end());
288 return mShaderProgramType[id];
289 }
290
291 private:
292 // Buffer map calls will map a buffer with correct offset, length, and access flags
293 BufferCalls mBufferMapCalls;
294 // Buffer unmap calls will bind and unmap a given buffer
295 BufferCalls mBufferUnmapCalls;
296
297 // Buffer binding calls to restore bindings recorded during MEC
298 std::vector<CallCapture> mBufferBindingCalls;
299
300 // Whether a given buffer was mapped at the start of the trace
301 BufferMapStatusMap mStartingBuffersMappedInitial;
302 // The status of buffer mapping throughout the trace, modified with each Map/Unmap call
303 BufferMapStatusMap mStartingBuffersMappedCurrent;
304
305 // Maximum accessed shader program ID.
306 uint32_t mMaxShaderPrograms = 0;
307
308 // Fence sync objects created during MEC setup
309 FenceSyncSet mStartingFenceSyncs;
310 // Fence sync regen calls will create a fence sync objects
311 FenceSyncCalls mFenceSyncRegenCalls;
312 // Fence syncs to regen are a list of starting fence sync objects that were deleted and need to
313 // be regen'ed.
314 FenceSyncSet mFenceSyncsToRegen;
315
316 // Default uniforms that were modified during the run
317 DefaultUniformLocationsPerProgramMap mDefaultUniformsToReset;
318 // Calls per default uniform to return to original state
319 DefaultUniformCallsPerProgramMap mDefaultUniformResetCalls;
320
321 // Base location of arrayed uniforms
322 DefaultUniformBaseLocationMap mDefaultUniformBaseLocations;
323
324 // Tracked resources per context
325 TrackedResourceArray mTrackedResourcesShared;
326 std::map<gl::ContextID, TrackedResourceArray> mTrackedResourcesPerContext;
327
328 std::map<EGLImage, egl::AttributeMap> mMatchImageToAttribs;
329 std::map<GLuint, egl::ImageID> mMatchTextureIDToImage;
330
331 std::map<gl::ShaderProgramID, ShaderProgramType> mShaderProgramType;
332 };
333
334 // Used by the CPP replay to filter out unnecessary code.
335 using HasResourceTypeMap = angle::PackedEnumBitSet<ResourceIDType>;
336
337 // Map of ResourceType to IDs and range of setup calls
338 using ResourceIDToSetupCallsMap =
339 PackedEnumMap<ResourceIDType, std::map<GLuint, gl::Range<size_t>>>;
340
341 // Map of buffer ID to offset and size used when mapped
342 using BufferDataMap = std::map<gl::BufferID, std::pair<GLintptr, GLsizeiptr>>;
343
344 // A dictionary of sources indexed by shader type.
345 using ProgramSources = gl::ShaderMap<std::string>;
346
347 // Maps from IDs to sources.
348 using ShaderSourceMap = std::map<gl::ShaderProgramID, std::string>;
349 using ProgramSourceMap = std::map<gl::ShaderProgramID, ProgramSources>;
350
351 // Map from textureID to level and data
352 using TextureLevels = std::map<GLint, std::vector<uint8_t>>;
353 using TextureLevelDataMap = std::map<gl::TextureID, TextureLevels>;
354
355 struct SurfaceParams
356 {
357 gl::Extents extents;
358 egl::ColorSpace colorSpace;
359 };
360
361 // Map from ContextID to SurfaceParams
362 using SurfaceParamsMap = std::map<gl::ContextID, SurfaceParams>;
363
364 using CallVector = std::vector<std::vector<CallCapture> *>;
365
366 // A map from API entry point to calls
367 using CallResetMap = std::map<angle::EntryPoint, std::vector<CallCapture>>;
368
369 using TextureBinding = std::pair<size_t, gl::TextureType>;
370 using TextureResetMap = std::map<TextureBinding, gl::TextureID>;
371
372 // StateResetHelper provides a simple way to track whether an entry point has been called during the
373 // trace, along with the reset calls to get it back to starting state. This is useful for things
374 // that are one dimensional, like context bindings or context state.
375 class StateResetHelper final : angle::NonCopyable
376 {
377 public:
378 StateResetHelper();
379 ~StateResetHelper();
380
getDirtyEntryPoints()381 const std::set<angle::EntryPoint> &getDirtyEntryPoints() const { return mDirtyEntryPoints; }
setEntryPointDirty(EntryPoint entryPoint)382 void setEntryPointDirty(EntryPoint entryPoint) { mDirtyEntryPoints.insert(entryPoint); }
383
getResetCalls()384 CallResetMap &getResetCalls() { return mResetCalls; }
getResetCalls()385 const CallResetMap &getResetCalls() const { return mResetCalls; }
386
387 void setDefaultResetCalls(const gl::Context *context, angle::EntryPoint);
388
getDirtyTextureBindings()389 const std::set<TextureBinding> &getDirtyTextureBindings() const
390 {
391 return mDirtyTextureBindings;
392 }
setTextureBindingDirty(size_t unit,gl::TextureType target)393 void setTextureBindingDirty(size_t unit, gl::TextureType target)
394 {
395 mDirtyTextureBindings.emplace(unit, target);
396 }
397
getResetTextureBindings()398 TextureResetMap &getResetTextureBindings() { return mResetTextureBindings; }
399
setResetActiveTexture(size_t textureID)400 void setResetActiveTexture(size_t textureID) { mResetActiveTexture = textureID; }
getResetActiveTexture()401 size_t getResetActiveTexture() { return mResetActiveTexture; }
402
403 private:
404 // Dirty state per entry point
405 std::set<angle::EntryPoint> mDirtyEntryPoints;
406
407 // Reset calls per API entry point
408 CallResetMap mResetCalls;
409
410 // Dirty state per texture binding
411 std::set<TextureBinding> mDirtyTextureBindings;
412
413 // Texture bindings and active texture to restore
414 TextureResetMap mResetTextureBindings;
415 size_t mResetActiveTexture = 0;
416 };
417
418 class FrameCapture final : angle::NonCopyable
419 {
420 public:
421 FrameCapture();
422 ~FrameCapture();
423
getSetupCalls()424 std::vector<CallCapture> &getSetupCalls() { return mSetupCalls; }
clearSetupCalls()425 void clearSetupCalls() { mSetupCalls.clear(); }
426
getStateResetHelper()427 StateResetHelper &getStateResetHelper() { return mStateResetHelper; }
428
429 void reset();
430
431 private:
432 std::vector<CallCapture> mSetupCalls;
433
434 StateResetHelper mStateResetHelper;
435 };
436
437 // Page range inside a coherent buffer
438 struct PageRange
439 {
440 PageRange(size_t start, size_t end);
441 ~PageRange();
442
443 // Relative start page
444 size_t start;
445
446 // First page after the relative end
447 size_t end;
448 };
449
450 // Memory address range defined by start and size
451 struct AddressRange
452 {
453 AddressRange();
454 AddressRange(uintptr_t start, size_t size);
455 ~AddressRange();
456
457 uintptr_t end();
458
459 uintptr_t start;
460 size_t size;
461 };
462
463 // Used to handle protection of buffers that overlap in pages.
464 enum class PageSharingType
465 {
466 NoneShared,
467 FirstShared,
468 LastShared,
469 FirstAndLastShared
470 };
471
472 class CoherentBuffer
473 {
474 public:
475 CoherentBuffer(uintptr_t start, size_t size, size_t pageSize, bool useShadowMemory);
476 ~CoherentBuffer();
477
478 // Sets the a range in the buffer clean and protects a selected range
479 void protectPageRange(const PageRange &pageRange);
480
481 // Sets all pages to clean and enables protection
482 void protectAll();
483
484 // Sets a page dirty state and sets it's protection
485 void setDirty(size_t relativePage, bool dirty);
486
487 // Shadow memory synchronization
488 void updateBufferMemory();
489 void updateShadowMemory();
490
491 // Removes protection
492 void removeProtection(PageSharingType sharingType);
493
494 bool contains(size_t page, size_t *relativePage);
495 bool isDirty();
496
497 // Returns dirty page ranges
498 std::vector<PageRange> getDirtyPageRanges();
499
500 // Calculates address range from page range
501 AddressRange getDirtyAddressRange(const PageRange &dirtyPageRange);
502 AddressRange getRange();
503
markShadowDirty()504 void markShadowDirty() { mShadowDirty = true; }
isShadowDirty()505 bool isShadowDirty() { return mShadowDirty; }
506
507 private:
508 // Actual buffer start and size
509 AddressRange mRange;
510
511 // Start and size of page aligned protected area
512 AddressRange mProtectionRange;
513
514 // Start and end of protection in relative pages, calculated from mProtectionRange.
515 size_t mProtectionStartPage;
516 size_t mProtectionEndPage;
517
518 size_t mPageCount;
519 size_t mPageSize;
520
521 // Clean pages are protected
522 std::vector<bool> mDirtyPages;
523
524 // shadow memory releated fields
525 bool mShadowMemoryEnabled;
526 uintptr_t mBufferStart;
527 void *mShadowMemory;
528 bool mShadowDirty;
529 };
530
531 class CoherentBufferTracker final : angle::NonCopyable
532 {
533 public:
534 CoherentBufferTracker();
535 ~CoherentBufferTracker();
536
537 bool isDirty(gl::BufferID id);
538 uintptr_t addBuffer(gl::BufferID id, uintptr_t start, size_t size);
539 void removeBuffer(gl::BufferID id);
540 void disable();
541 void enable();
542 void onEndFrame();
543 bool haveBuffer(gl::BufferID id);
isShadowMemoryEnabled()544 bool isShadowMemoryEnabled() { return mShadowMemoryEnabled; }
enableShadowMemory()545 void enableShadowMemory() { mShadowMemoryEnabled = true; }
546 void maybeUpdateShadowMemory();
547 void markAllShadowDirty();
548 // Determine whether memory protection can be used directly on graphics memory
549 bool canProtectDirectly(gl::Context *context);
550
551 private:
552 // Detect overlapping pages when removing protection
553 PageSharingType doesBufferSharePage(gl::BufferID id);
554
555 // Returns a map to found buffers and the corresponding pages for a given address.
556 // For addresses that are in a page shared by 2 buffers, 2 results are returned.
557 HashMap<std::shared_ptr<CoherentBuffer>, size_t> getBufferPagesForAddress(uintptr_t address);
558 PageFaultHandlerRangeType handleWrite(uintptr_t address);
559
560 public:
561 angle::SimpleMutex mMutex;
562 HashMap<GLuint, std::shared_ptr<CoherentBuffer>> mBuffers;
563
564 private:
565 bool mEnabled;
566 std::unique_ptr<PageFaultHandler> mPageFaultHandler;
567 size_t mPageSize;
568
569 bool mShadowMemoryEnabled;
570 };
571
572 // Shared class for any items that need to be tracked by FrameCapture across shared contexts
573 class FrameCaptureShared final : angle::NonCopyable
574 {
575 public:
576 FrameCaptureShared();
577 ~FrameCaptureShared();
578
579 void captureCall(gl::Context *context, CallCapture &&call, bool isCallValid);
580 void checkForCaptureTrigger();
581 void onEndFrame(gl::Context *context);
582 void onDestroyContext(const gl::Context *context);
583 void onMakeCurrent(const gl::Context *context, const egl::Surface *drawSurface);
enabled()584 bool enabled() const { return mEnabled; }
585
586 bool isCapturing() const;
587 uint32_t getFrameCount() const;
588
589 // Returns a frame index starting from "1" as the first frame.
590 uint32_t getReplayFrameIndex() const;
591
592 void trackBufferMapping(const gl::Context *context,
593 CallCapture *call,
594 gl::BufferID id,
595 gl::Buffer *buffer,
596 GLintptr offset,
597 GLsizeiptr length,
598 bool writable,
599 bool coherent);
600
601 void trackTextureUpdate(const gl::Context *context, const CallCapture &call);
602 void trackImageUpdate(const gl::Context *context, const CallCapture &call);
603 void trackDefaultUniformUpdate(const gl::Context *context, const CallCapture &call);
604 void trackVertexArrayUpdate(const gl::Context *context, const CallCapture &call);
605
606 const std::string &getShaderSource(gl::ShaderProgramID id) const;
607 void setShaderSource(gl::ShaderProgramID id, std::string sources);
608
609 const ProgramSources &getProgramSources(gl::ShaderProgramID id) const;
610 void setProgramSources(gl::ShaderProgramID id, ProgramSources sources);
611
612 // Load data from a previously stored texture level
613 const std::vector<uint8_t> &retrieveCachedTextureLevel(gl::TextureID id,
614 gl::TextureTarget target,
615 GLint level);
616
617 // Create new texture level data and copy the source into it
618 void copyCachedTextureLevel(const gl::Context *context,
619 gl::TextureID srcID,
620 GLint srcLevel,
621 gl::TextureID dstID,
622 GLint dstLevel,
623 const CallCapture &call);
624
625 // Create the location that should be used to cache texture level data
626 std::vector<uint8_t> &getCachedTextureLevelData(gl::Texture *texture,
627 gl::TextureTarget target,
628 GLint level,
629 EntryPoint entryPoint);
630
631 // Capture coherent buffer storages
632 void captureCoherentBufferSnapshot(const gl::Context *context, gl::BufferID bufferID);
633
634 // Remove any cached texture levels on deletion
635 void deleteCachedTextureLevelData(gl::TextureID id);
636
eraseBufferDataMapEntry(const gl::BufferID bufferId)637 void eraseBufferDataMapEntry(const gl::BufferID bufferId)
638 {
639 const auto &bufferDataInfo = mBufferDataMap.find(bufferId);
640 if (bufferDataInfo != mBufferDataMap.end())
641 {
642 mBufferDataMap.erase(bufferDataInfo);
643 }
644 }
645
hasBufferData(gl::BufferID bufferID)646 bool hasBufferData(gl::BufferID bufferID)
647 {
648 const auto &bufferDataInfo = mBufferDataMap.find(bufferID);
649 if (bufferDataInfo != mBufferDataMap.end())
650 {
651 return true;
652 }
653 return false;
654 }
655
getBufferDataOffsetAndLength(gl::BufferID bufferID)656 std::pair<GLintptr, GLsizeiptr> getBufferDataOffsetAndLength(gl::BufferID bufferID)
657 {
658 const auto &bufferDataInfo = mBufferDataMap.find(bufferID);
659 ASSERT(bufferDataInfo != mBufferDataMap.end());
660 return bufferDataInfo->second;
661 }
662
setCaptureActive()663 void setCaptureActive() { mCaptureActive = true; }
setCaptureInactive()664 void setCaptureInactive() { mCaptureActive = false; }
isCaptureActive()665 bool isCaptureActive() { return mCaptureActive; }
usesMidExecutionCapture()666 bool usesMidExecutionCapture() { return mCaptureStartFrame > 1; }
667
getWindowSurfaceContextID()668 gl::ContextID getWindowSurfaceContextID() const { return mWindowSurfaceContextID; }
669
670 void markResourceSetupCallsInactive(std::vector<CallCapture> *setupCalls,
671 ResourceIDType type,
672 GLuint id,
673 gl::Range<size_t> range);
674
updateReadBufferSize(size_t readBufferSize)675 void updateReadBufferSize(size_t readBufferSize)
676 {
677 mReadBufferSize = std::max(mReadBufferSize, readBufferSize);
678 }
679
680 template <typename ResourceType>
handleGennedResource(const gl::Context * context,ResourceType resourceID)681 void handleGennedResource(const gl::Context *context, ResourceType resourceID)
682 {
683 if (isCaptureActive())
684 {
685 ResourceIDType idType = GetResourceIDTypeFromType<ResourceType>::IDType;
686 TrackedResource &tracker = mResourceTracker.getTrackedResource(context->id(), idType);
687 tracker.setGennedResource(resourceID.value);
688 }
689 }
690
691 template <typename ResourceType>
resourceIsGenerated(const gl::Context * context,ResourceType resourceID)692 bool resourceIsGenerated(const gl::Context *context, ResourceType resourceID)
693 {
694 ResourceIDType idType = GetResourceIDTypeFromType<ResourceType>::IDType;
695 TrackedResource &tracker = mResourceTracker.getTrackedResource(context->id(), idType);
696 return tracker.resourceIsGenerated(resourceID.value);
697 }
698
699 template <typename ResourceType>
handleDeletedResource(const gl::Context * context,ResourceType resourceID)700 void handleDeletedResource(const gl::Context *context, ResourceType resourceID)
701 {
702 if (isCaptureActive())
703 {
704 ResourceIDType idType = GetResourceIDTypeFromType<ResourceType>::IDType;
705 TrackedResource &tracker = mResourceTracker.getTrackedResource(context->id(), idType);
706 tracker.setDeletedResource(resourceID.value);
707 }
708 }
709
710 void *maybeGetShadowMemoryPointer(gl::Buffer *buffer, GLsizeiptr length, GLbitfield access);
711 void determineMemoryProtectionSupport(gl::Context *context);
712
getFrameCaptureMutex()713 angle::SimpleMutex &getFrameCaptureMutex() { return mFrameCaptureMutex; }
714
setDeferredLinkProgram(gl::ShaderProgramID programID)715 void setDeferredLinkProgram(gl::ShaderProgramID programID)
716 {
717 mDeferredLinkPrograms.emplace(programID);
718 }
isDeferredLinkProgram(gl::ShaderProgramID programID)719 bool isDeferredLinkProgram(gl::ShaderProgramID programID)
720 {
721 return (mDeferredLinkPrograms.find(programID) != mDeferredLinkPrograms.end());
722 }
723
724 private:
725 void writeJSON(const gl::Context *context);
726 void writeCppReplayIndexFiles(const gl::Context *context, bool writeResetContextCall);
727 void writeMainContextCppReplay(const gl::Context *context,
728 const std::vector<CallCapture> &setupCalls,
729 StateResetHelper &StateResetHelper);
730
731 void captureClientArraySnapshot(const gl::Context *context,
732 size_t vertexCount,
733 size_t instanceCount);
734 void captureMappedBufferSnapshot(const gl::Context *context, const CallCapture &call);
735
736 void copyCompressedTextureData(const gl::Context *context, const CallCapture &call);
737 void captureCompressedTextureData(const gl::Context *context, const CallCapture &call);
738
739 void reset();
740 void maybeOverrideEntryPoint(const gl::Context *context,
741 CallCapture &call,
742 std::vector<CallCapture> &newCalls);
743 void maybeCapturePreCallUpdates(const gl::Context *context,
744 CallCapture &call,
745 std::vector<CallCapture> *shareGroupSetupCalls,
746 ResourceIDToSetupCallsMap *resourceIDToSetupCalls);
747 template <typename ParamValueType>
748 void maybeGenResourceOnBind(const gl::Context *context, CallCapture &call);
749 void maybeCapturePostCallUpdates(const gl::Context *context);
750 void maybeCaptureDrawArraysClientData(const gl::Context *context,
751 CallCapture &call,
752 size_t instanceCount);
753 void maybeCaptureDrawElementsClientData(const gl::Context *context,
754 CallCapture &call,
755 size_t instanceCount);
756 void maybeCaptureCoherentBuffers(const gl::Context *context);
757 void captureCustomMapBufferFromContext(const gl::Context *context,
758 const char *entryPointName,
759 CallCapture &call,
760 std::vector<CallCapture> &callsOut);
761 void updateCopyImageSubData(CallCapture &call);
762 void overrideProgramBinary(const gl::Context *context,
763 CallCapture &call,
764 std::vector<CallCapture> &outCalls);
765 void updateResourceCountsFromParamCapture(const ParamCapture ¶m, ResourceIDType idType);
766 void updateResourceCountsFromCallCapture(const CallCapture &call);
767
768 void runMidExecutionCapture(gl::Context *context);
769
770 void scanSetupCalls(std::vector<CallCapture> &setupCalls);
771
772 std::vector<CallCapture> mFrameCalls;
773
774 // We save one large buffer of binary data for the whole CPP replay.
775 // This simplifies a lot of file management.
776 std::vector<uint8_t> mBinaryData;
777
778 bool mEnabled;
779 bool mSerializeStateEnabled;
780 std::string mOutDirectory;
781 std::string mCaptureLabel;
782 bool mCompression;
783 gl::AttribArray<int> mClientVertexArrayMap;
784 uint32_t mFrameIndex;
785 uint32_t mCaptureStartFrame;
786 uint32_t mCaptureEndFrame;
787 bool mIsFirstFrame = true;
788 bool mWroteIndexFile = false;
789 SurfaceParamsMap mDrawSurfaceParams;
790 gl::AttribArray<size_t> mClientArraySizes;
791 size_t mReadBufferSize;
792 size_t mResourceIDBufferSize;
793 HasResourceTypeMap mHasResourceType;
794 ResourceIDToSetupCallsMap mResourceIDToSetupCalls;
795 BufferDataMap mBufferDataMap;
796 bool mValidateSerializedState = false;
797 std::string mValidationExpression;
798 PackedEnumMap<ResourceIDType, uint32_t> mMaxAccessedResourceIDs;
799 CoherentBufferTracker mCoherentBufferTracker;
800 angle::SimpleMutex mFrameCaptureMutex;
801
802 ResourceTracker mResourceTracker;
803 ReplayWriter mReplayWriter;
804
805 // If you don't know which frame you want to start capturing at, use the capture trigger.
806 // Initialize it to the number of frames you want to capture, and then clear the value to 0 when
807 // you reach the content you want to capture. Currently only available on Android.
808 uint32_t mCaptureTrigger;
809
810 bool mCaptureActive;
811 std::vector<uint32_t> mActiveFrameIndices;
812
813 // Cache most recently compiled and linked sources.
814 ShaderSourceMap mCachedShaderSource;
815 ProgramSourceMap mCachedProgramSources;
816
817 // Set of programs which were created but not linked before capture was started
818 std::set<gl::ShaderProgramID> mDeferredLinkPrograms;
819
820 gl::ContextID mWindowSurfaceContextID;
821
822 std::vector<CallCapture> mShareGroupSetupCalls;
823 // Track which Contexts were created and made current at least once before MEC,
824 // requiring setup for replay
825 std::unordered_set<GLuint> mActiveContexts;
826
827 // Invalid call counts per entry point while capture is active and inactive.
828 std::unordered_map<EntryPoint, size_t> mInvalidCallCountsActive;
829 std::unordered_map<EntryPoint, size_t> mInvalidCallCountsInactive;
830 };
831
832 template <typename CaptureFuncT, typename... ArgsT>
CaptureGLCallToFrameCapture(CaptureFuncT captureFunc,bool isCallValid,gl::Context * context,ArgsT...captureParams)833 void CaptureGLCallToFrameCapture(CaptureFuncT captureFunc,
834 bool isCallValid,
835 gl::Context *context,
836 ArgsT... captureParams)
837 {
838 FrameCaptureShared *frameCaptureShared = context->getShareGroup()->getFrameCaptureShared();
839
840 // EGL calls are protected by the global context mutex but only a subset of GL calls
841 // are so protected. Ensure FrameCaptureShared access thread safety by using a
842 // frame-capture only mutex.
843 std::lock_guard<angle::SimpleMutex> lock(frameCaptureShared->getFrameCaptureMutex());
844
845 if (!frameCaptureShared->isCapturing())
846 {
847 return;
848 }
849
850 CallCapture call = captureFunc(context->getState(), isCallValid, captureParams...);
851 frameCaptureShared->captureCall(context, std::move(call), isCallValid);
852 }
853
854 template <typename FirstT, typename... OthersT>
GetEGLDisplayArg(FirstT display,OthersT...others)855 egl::Display *GetEGLDisplayArg(FirstT display, OthersT... others)
856 {
857 if constexpr (std::is_same<egl::Display *, FirstT>::value)
858 {
859 return display;
860 }
861 return nullptr;
862 }
863
864 template <typename CaptureFuncT, typename... ArgsT>
CaptureEGLCallToFrameCapture(CaptureFuncT captureFunc,bool isCallValid,egl::Thread * thread,ArgsT...captureParams)865 void CaptureEGLCallToFrameCapture(CaptureFuncT captureFunc,
866 bool isCallValid,
867 egl::Thread *thread,
868 ArgsT... captureParams)
869 {
870 gl::Context *context = thread->getContext();
871 if (!context)
872 {
873 // Get a valid context from the display argument if no context is associated with this
874 // thread
875 egl::Display *display = GetEGLDisplayArg(captureParams...);
876 if (display)
877 {
878 for (const auto &contextIter : display->getState().contextMap)
879 {
880 context = contextIter.second;
881 break;
882 }
883 }
884 if (!context)
885 {
886 return;
887 }
888 }
889 std::lock_guard<egl::ContextMutex> lock(context->getContextMutex());
890
891 angle::FrameCaptureShared *frameCaptureShared =
892 context->getShareGroup()->getFrameCaptureShared();
893 if (!frameCaptureShared->isCapturing())
894 {
895 return;
896 }
897
898 angle::CallCapture call = captureFunc(thread, isCallValid, captureParams...);
899 frameCaptureShared->captureCall(context, std::move(call), true);
900 }
901
902 // Pointer capture helpers.
903 void CaptureMemory(const void *source, size_t size, ParamCapture *paramCapture);
904 void CaptureString(const GLchar *str, ParamCapture *paramCapture);
905 void CaptureStringLimit(const GLchar *str, uint32_t limit, ParamCapture *paramCapture);
906 void CaptureVertexPointerGLES1(const gl::State &glState,
907 gl::ClientVertexArrayType type,
908 const void *pointer,
909 ParamCapture *paramCapture);
910
911 gl::Program *GetProgramForCapture(const gl::State &glState, gl::ShaderProgramID handle);
912
913 // For GetIntegerv, GetFloatv, etc.
914 void CaptureGetParameter(const gl::State &glState,
915 GLenum pname,
916 size_t typeSize,
917 ParamCapture *paramCapture);
918
919 void CaptureGetActiveUniformBlockivParameters(const gl::State &glState,
920 gl::ShaderProgramID handle,
921 gl::UniformBlockIndex uniformBlockIndex,
922 GLenum pname,
923 ParamCapture *paramCapture);
924
925 template <typename T>
CaptureClearBufferValue(GLenum buffer,const T * value,ParamCapture * paramCapture)926 void CaptureClearBufferValue(GLenum buffer, const T *value, ParamCapture *paramCapture)
927 {
928 // Per the spec, color buffers have a vec4, the rest a single value
929 uint32_t valueSize = (buffer == GL_COLOR) ? 4 : 1;
930 CaptureMemory(value, valueSize * sizeof(T), paramCapture);
931 }
932
933 void CaptureGenHandlesImpl(GLsizei n, GLuint *handles, ParamCapture *paramCapture);
934
935 template <typename T>
CaptureGenHandles(GLsizei n,T * handles,ParamCapture * paramCapture)936 void CaptureGenHandles(GLsizei n, T *handles, ParamCapture *paramCapture)
937 {
938 paramCapture->dataNElements = n;
939 CaptureGenHandlesImpl(n, reinterpret_cast<GLuint *>(handles), paramCapture);
940 }
941
942 template <typename T>
CaptureArray(T * elements,GLsizei n,ParamCapture * paramCapture)943 void CaptureArray(T *elements, GLsizei n, ParamCapture *paramCapture)
944 {
945 paramCapture->dataNElements = n;
946 CaptureMemory(elements, n * sizeof(T), paramCapture);
947 }
948
949 void CaptureShaderStrings(GLsizei count,
950 const GLchar *const *strings,
951 const GLint *length,
952 ParamCapture *paramCapture);
953
954 } // namespace angle
955
956 template <typename T>
CaptureTextureAndSamplerParameter_params(GLenum pname,const T * param,angle::ParamCapture * paramCapture)957 void CaptureTextureAndSamplerParameter_params(GLenum pname,
958 const T *param,
959 angle::ParamCapture *paramCapture)
960 {
961 if (pname == GL_TEXTURE_BORDER_COLOR || pname == GL_TEXTURE_CROP_RECT_OES)
962 {
963 CaptureMemory(param, sizeof(T) * 4, paramCapture);
964 }
965 else
966 {
967 CaptureMemory(param, sizeof(T), paramCapture);
968 }
969 }
970
971 namespace egl
972 {
973 angle::ParamCapture CaptureAttributeMap(const egl::AttributeMap &attribMap);
974 } // namespace egl
975
976 #endif // LIBANGLE_FRAME_CAPTURE_H_
977