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 using BufferBindingPair = std::pair<gl::BufferBinding, gl::BufferID>;
373
374 // StateResetHelper provides a simple way to track whether an entry point has been called during the
375 // trace, along with the reset calls to get it back to starting state. This is useful for things
376 // that are one dimensional, like context bindings or context state.
377 class StateResetHelper final : angle::NonCopyable
378 {
379 public:
380 StateResetHelper();
381 ~StateResetHelper();
382
getDirtyEntryPoints()383 const std::set<angle::EntryPoint> &getDirtyEntryPoints() const { return mDirtyEntryPoints; }
setEntryPointDirty(EntryPoint entryPoint)384 void setEntryPointDirty(EntryPoint entryPoint) { mDirtyEntryPoints.insert(entryPoint); }
385
getResetCalls()386 CallResetMap &getResetCalls() { return mResetCalls; }
getResetCalls()387 const CallResetMap &getResetCalls() const { return mResetCalls; }
388
389 void setDefaultResetCalls(const gl::Context *context, angle::EntryPoint);
390
getDirtyTextureBindings()391 const std::set<TextureBinding> &getDirtyTextureBindings() const
392 {
393 return mDirtyTextureBindings;
394 }
setTextureBindingDirty(size_t unit,gl::TextureType target)395 void setTextureBindingDirty(size_t unit, gl::TextureType target)
396 {
397 mDirtyTextureBindings.emplace(unit, target);
398 }
399
getResetTextureBindings()400 TextureResetMap &getResetTextureBindings() { return mResetTextureBindings; }
401
setResetActiveTexture(size_t textureID)402 void setResetActiveTexture(size_t textureID) { mResetActiveTexture = textureID; }
getResetActiveTexture()403 size_t getResetActiveTexture() { return mResetActiveTexture; }
404
getDirtyBufferBindings()405 const std::set<gl::BufferBinding> &getDirtyBufferBindings() const
406 {
407 return mDirtyBufferBindings;
408 }
setBufferBindingDirty(gl::BufferBinding binding)409 void setBufferBindingDirty(gl::BufferBinding binding) { mDirtyBufferBindings.insert(binding); }
410
getStartingBufferBindings()411 const std::set<BufferBindingPair> &getStartingBufferBindings() const
412 {
413 return mStartingBufferBindings;
414 }
setStartingBufferBinding(gl::BufferBinding binding,gl::BufferID bufferID)415 void setStartingBufferBinding(gl::BufferBinding binding, gl::BufferID bufferID)
416 {
417 mStartingBufferBindings.insert({binding, bufferID});
418 }
419
420 private:
421 // Dirty state per entry point
422 std::set<angle::EntryPoint> mDirtyEntryPoints;
423
424 // Reset calls per API entry point
425 CallResetMap mResetCalls;
426
427 // Dirty state per texture binding
428 std::set<TextureBinding> mDirtyTextureBindings;
429
430 // Texture bindings and active texture to restore
431 TextureResetMap mResetTextureBindings;
432 size_t mResetActiveTexture = 0;
433
434 // Starting and dirty buffer bindings
435 std::set<BufferBindingPair> mStartingBufferBindings;
436 std::set<gl::BufferBinding> mDirtyBufferBindings;
437 };
438
439 class FrameCapture final : angle::NonCopyable
440 {
441 public:
442 FrameCapture();
443 ~FrameCapture();
444
getSetupCalls()445 std::vector<CallCapture> &getSetupCalls() { return mSetupCalls; }
clearSetupCalls()446 void clearSetupCalls() { mSetupCalls.clear(); }
447
getStateResetHelper()448 StateResetHelper &getStateResetHelper() { return mStateResetHelper; }
449
450 void reset();
451
452 private:
453 std::vector<CallCapture> mSetupCalls;
454
455 StateResetHelper mStateResetHelper;
456 };
457
458 // Page range inside a coherent buffer
459 struct PageRange
460 {
461 PageRange(size_t start, size_t end);
462 ~PageRange();
463
464 // Relative start page
465 size_t start;
466
467 // First page after the relative end
468 size_t end;
469 };
470
471 // Memory address range defined by start and size
472 struct AddressRange
473 {
474 AddressRange();
475 AddressRange(uintptr_t start, size_t size);
476 ~AddressRange();
477
478 uintptr_t end();
479
480 uintptr_t start;
481 size_t size;
482 };
483
484 // Used to handle protection of buffers that overlap in pages.
485 enum class PageSharingType
486 {
487 NoneShared,
488 FirstShared,
489 LastShared,
490 FirstAndLastShared
491 };
492
493 class CoherentBuffer
494 {
495 public:
496 CoherentBuffer(uintptr_t start, size_t size, size_t pageSize, bool useShadowMemory);
497 ~CoherentBuffer();
498
499 // Sets the a range in the buffer clean and protects a selected range
500 void protectPageRange(const PageRange &pageRange);
501
502 // Sets all pages to clean and enables protection
503 void protectAll();
504
505 // Sets a page dirty state and sets it's protection
506 void setDirty(size_t relativePage, bool dirty);
507
508 // Shadow memory synchronization
509 void updateBufferMemory();
510 void updateShadowMemory();
511
512 // Removes protection
513 void removeProtection(PageSharingType sharingType);
514
515 bool contains(size_t page, size_t *relativePage);
516 bool isDirty();
517
518 // Returns dirty page ranges
519 std::vector<PageRange> getDirtyPageRanges();
520
521 // Calculates address range from page range
522 AddressRange getDirtyAddressRange(const PageRange &dirtyPageRange);
523 AddressRange getRange();
524
markShadowDirty()525 void markShadowDirty() { mShadowDirty = true; }
isShadowDirty()526 bool isShadowDirty() { return mShadowDirty; }
527
528 private:
529 // Actual buffer start and size
530 AddressRange mRange;
531
532 // Start and size of page aligned protected area
533 AddressRange mProtectionRange;
534
535 // Start and end of protection in relative pages, calculated from mProtectionRange.
536 size_t mProtectionStartPage;
537 size_t mProtectionEndPage;
538
539 size_t mPageCount;
540 size_t mPageSize;
541
542 // Clean pages are protected
543 std::vector<bool> mDirtyPages;
544
545 // shadow memory releated fields
546 bool mShadowMemoryEnabled;
547 uintptr_t mBufferStart;
548 void *mShadowMemory;
549 bool mShadowDirty;
550 };
551
552 class CoherentBufferTracker final : angle::NonCopyable
553 {
554 public:
555 CoherentBufferTracker();
556 ~CoherentBufferTracker();
557
558 bool isDirty(gl::BufferID id);
559 uintptr_t addBuffer(gl::BufferID id, uintptr_t start, size_t size);
560 void removeBuffer(gl::BufferID id);
561 void disable();
562 void enable();
563 void onEndFrame();
564 bool haveBuffer(gl::BufferID id);
isShadowMemoryEnabled()565 bool isShadowMemoryEnabled() { return mShadowMemoryEnabled; }
enableShadowMemory()566 void enableShadowMemory() { mShadowMemoryEnabled = true; }
567 void maybeUpdateShadowMemory();
568 void markAllShadowDirty();
569 // Determine whether memory protection can be used directly on graphics memory
570 bool canProtectDirectly(gl::Context *context);
571
572 private:
573 // Detect overlapping pages when removing protection
574 PageSharingType doesBufferSharePage(gl::BufferID id);
575
576 // Returns a map to found buffers and the corresponding pages for a given address.
577 // For addresses that are in a page shared by 2 buffers, 2 results are returned.
578 HashMap<std::shared_ptr<CoherentBuffer>, size_t> getBufferPagesForAddress(uintptr_t address);
579 PageFaultHandlerRangeType handleWrite(uintptr_t address);
580
581 public:
582 angle::SimpleMutex mMutex;
583 HashMap<GLuint, std::shared_ptr<CoherentBuffer>> mBuffers;
584
585 private:
586 bool mEnabled;
587 std::unique_ptr<PageFaultHandler> mPageFaultHandler;
588 size_t mPageSize;
589
590 bool mShadowMemoryEnabled;
591 };
592
593 // Shared class for any items that need to be tracked by FrameCapture across shared contexts
594 class FrameCaptureShared final : angle::NonCopyable
595 {
596 public:
597 FrameCaptureShared();
598 ~FrameCaptureShared();
599
600 void captureCall(gl::Context *context, CallCapture &&call, bool isCallValid);
601 void checkForCaptureTrigger();
602 void onEndFrame(gl::Context *context);
603 void onDestroyContext(const gl::Context *context);
604 void onMakeCurrent(const gl::Context *context, const egl::Surface *drawSurface);
enabled()605 bool enabled() const { return mEnabled; }
606
607 bool isCapturing() const;
608 uint32_t getFrameCount() const;
609
610 // Returns a frame index starting from "1" as the first frame.
611 uint32_t getReplayFrameIndex() const;
612
613 void trackBufferMapping(const gl::Context *context,
614 CallCapture *call,
615 gl::BufferID id,
616 gl::Buffer *buffer,
617 GLintptr offset,
618 GLsizeiptr length,
619 bool writable,
620 bool coherent);
621
622 void trackTextureUpdate(const gl::Context *context, const CallCapture &call);
623 void trackImageUpdate(const gl::Context *context, const CallCapture &call);
624 void trackDefaultUniformUpdate(const gl::Context *context, const CallCapture &call);
625 void trackVertexArrayUpdate(const gl::Context *context, const CallCapture &call);
626
627 const std::string &getShaderSource(gl::ShaderProgramID id) const;
628 void setShaderSource(gl::ShaderProgramID id, std::string sources);
629
630 const ProgramSources &getProgramSources(gl::ShaderProgramID id) const;
631 void setProgramSources(gl::ShaderProgramID id, ProgramSources sources);
632
633 // Load data from a previously stored texture level
634 const std::vector<uint8_t> &retrieveCachedTextureLevel(gl::TextureID id,
635 gl::TextureTarget target,
636 GLint level);
637
638 // Create new texture level data and copy the source into it
639 void copyCachedTextureLevel(const gl::Context *context,
640 gl::TextureID srcID,
641 GLint srcLevel,
642 gl::TextureID dstID,
643 GLint dstLevel,
644 const CallCapture &call);
645
646 // Create the location that should be used to cache texture level data
647 std::vector<uint8_t> &getCachedTextureLevelData(gl::Texture *texture,
648 gl::TextureTarget target,
649 GLint level,
650 EntryPoint entryPoint);
651
652 // Capture coherent buffer storages
653 void captureCoherentBufferSnapshot(const gl::Context *context, gl::BufferID bufferID);
654
655 // Remove any cached texture levels on deletion
656 void deleteCachedTextureLevelData(gl::TextureID id);
657
eraseBufferDataMapEntry(const gl::BufferID bufferId)658 void eraseBufferDataMapEntry(const gl::BufferID bufferId)
659 {
660 const auto &bufferDataInfo = mBufferDataMap.find(bufferId);
661 if (bufferDataInfo != mBufferDataMap.end())
662 {
663 mBufferDataMap.erase(bufferDataInfo);
664 }
665 }
666
hasBufferData(gl::BufferID bufferID)667 bool hasBufferData(gl::BufferID bufferID)
668 {
669 const auto &bufferDataInfo = mBufferDataMap.find(bufferID);
670 if (bufferDataInfo != mBufferDataMap.end())
671 {
672 return true;
673 }
674 return false;
675 }
676
getBufferDataOffsetAndLength(gl::BufferID bufferID)677 std::pair<GLintptr, GLsizeiptr> getBufferDataOffsetAndLength(gl::BufferID bufferID)
678 {
679 const auto &bufferDataInfo = mBufferDataMap.find(bufferID);
680 ASSERT(bufferDataInfo != mBufferDataMap.end());
681 return bufferDataInfo->second;
682 }
683
setCaptureActive()684 void setCaptureActive() { mCaptureActive = true; }
setCaptureInactive()685 void setCaptureInactive() { mCaptureActive = false; }
isCaptureActive()686 bool isCaptureActive() { return mCaptureActive; }
usesMidExecutionCapture()687 bool usesMidExecutionCapture() { return mCaptureStartFrame > 1; }
688
getWindowSurfaceContextID()689 gl::ContextID getWindowSurfaceContextID() const { return mWindowSurfaceContextID; }
690
691 void markResourceSetupCallsInactive(std::vector<CallCapture> *setupCalls,
692 ResourceIDType type,
693 GLuint id,
694 gl::Range<size_t> range);
695
updateReadBufferSize(size_t readBufferSize)696 void updateReadBufferSize(size_t readBufferSize)
697 {
698 mReadBufferSize = std::max(mReadBufferSize, readBufferSize);
699 }
700
701 template <typename ResourceType>
handleGennedResource(const gl::Context * context,ResourceType resourceID)702 void handleGennedResource(const gl::Context *context, ResourceType resourceID)
703 {
704 if (isCaptureActive())
705 {
706 ResourceIDType idType = GetResourceIDTypeFromType<ResourceType>::IDType;
707 TrackedResource &tracker = mResourceTracker.getTrackedResource(context->id(), idType);
708 tracker.setGennedResource(resourceID.value);
709 }
710 }
711
712 template <typename ResourceType>
resourceIsGenerated(const gl::Context * context,ResourceType resourceID)713 bool resourceIsGenerated(const gl::Context *context, ResourceType resourceID)
714 {
715 ResourceIDType idType = GetResourceIDTypeFromType<ResourceType>::IDType;
716 TrackedResource &tracker = mResourceTracker.getTrackedResource(context->id(), idType);
717 return tracker.resourceIsGenerated(resourceID.value);
718 }
719
720 template <typename ResourceType>
handleDeletedResource(const gl::Context * context,ResourceType resourceID)721 void handleDeletedResource(const gl::Context *context, ResourceType resourceID)
722 {
723 if (isCaptureActive())
724 {
725 ResourceIDType idType = GetResourceIDTypeFromType<ResourceType>::IDType;
726 TrackedResource &tracker = mResourceTracker.getTrackedResource(context->id(), idType);
727 tracker.setDeletedResource(resourceID.value);
728 }
729 }
730
731 void *maybeGetShadowMemoryPointer(gl::Buffer *buffer, GLsizeiptr length, GLbitfield access);
732 void determineMemoryProtectionSupport(gl::Context *context);
733
getFrameCaptureMutex()734 angle::SimpleMutex &getFrameCaptureMutex() { return mFrameCaptureMutex; }
735
setDeferredLinkProgram(gl::ShaderProgramID programID)736 void setDeferredLinkProgram(gl::ShaderProgramID programID)
737 {
738 mDeferredLinkPrograms.emplace(programID);
739 }
isDeferredLinkProgram(gl::ShaderProgramID programID)740 bool isDeferredLinkProgram(gl::ShaderProgramID programID)
741 {
742 return (mDeferredLinkPrograms.find(programID) != mDeferredLinkPrograms.end());
743 }
744
745 private:
746 void writeJSON(const gl::Context *context);
747 void writeCppReplayIndexFiles(const gl::Context *context, bool writeResetContextCall);
748 void writeMainContextCppReplay(const gl::Context *context,
749 const std::vector<CallCapture> &setupCalls,
750 StateResetHelper &StateResetHelper);
751
752 void captureClientArraySnapshot(const gl::Context *context,
753 size_t vertexCount,
754 size_t instanceCount);
755 void captureMappedBufferSnapshot(const gl::Context *context, const CallCapture &call);
756
757 void copyCompressedTextureData(const gl::Context *context, const CallCapture &call);
758 void captureCompressedTextureData(const gl::Context *context, const CallCapture &call);
759
760 void reset();
761 void maybeOverrideEntryPoint(const gl::Context *context,
762 CallCapture &call,
763 std::vector<CallCapture> &newCalls);
764 void maybeCapturePreCallUpdates(const gl::Context *context,
765 CallCapture &call,
766 std::vector<CallCapture> *shareGroupSetupCalls,
767 ResourceIDToSetupCallsMap *resourceIDToSetupCalls);
768 template <typename ParamValueType>
769 void maybeGenResourceOnBind(const gl::Context *context, CallCapture &call);
770 void maybeCapturePostCallUpdates(const gl::Context *context);
771 void maybeCaptureDrawArraysClientData(const gl::Context *context,
772 CallCapture &call,
773 size_t instanceCount);
774 void maybeCaptureDrawElementsClientData(const gl::Context *context,
775 CallCapture &call,
776 size_t instanceCount);
777 void maybeCaptureCoherentBuffers(const gl::Context *context);
778 void captureCustomMapBufferFromContext(const gl::Context *context,
779 const char *entryPointName,
780 CallCapture &call,
781 std::vector<CallCapture> &callsOut);
782 void updateCopyImageSubData(CallCapture &call);
783 void overrideProgramBinary(const gl::Context *context,
784 CallCapture &call,
785 std::vector<CallCapture> &outCalls);
786 void updateResourceCountsFromParamCapture(const ParamCapture ¶m, ResourceIDType idType);
787 void updateResourceCountsFromCallCapture(const CallCapture &call);
788
789 void runMidExecutionCapture(gl::Context *context);
790
791 void scanSetupCalls(std::vector<CallCapture> &setupCalls);
792
793 std::vector<CallCapture> mFrameCalls;
794
795 // We save one large buffer of binary data for the whole CPP replay.
796 // This simplifies a lot of file management.
797 std::vector<uint8_t> mBinaryData;
798
799 bool mEnabled;
800 bool mSerializeStateEnabled;
801 std::string mOutDirectory;
802 std::string mCaptureLabel;
803 bool mCompression;
804 gl::AttribArray<int> mClientVertexArrayMap;
805 uint32_t mFrameIndex;
806 uint32_t mCaptureStartFrame;
807 uint32_t mCaptureEndFrame;
808 bool mIsFirstFrame = true;
809 bool mWroteIndexFile = false;
810 SurfaceParamsMap mDrawSurfaceParams;
811 gl::AttribArray<size_t> mClientArraySizes;
812 size_t mReadBufferSize;
813 size_t mResourceIDBufferSize;
814 HasResourceTypeMap mHasResourceType;
815 ResourceIDToSetupCallsMap mResourceIDToSetupCalls;
816 BufferDataMap mBufferDataMap;
817 bool mValidateSerializedState = false;
818 std::string mValidationExpression;
819 PackedEnumMap<ResourceIDType, uint32_t> mMaxAccessedResourceIDs;
820 CoherentBufferTracker mCoherentBufferTracker;
821 angle::SimpleMutex mFrameCaptureMutex;
822
823 ResourceTracker mResourceTracker;
824 ReplayWriter mReplayWriter;
825
826 // If you don't know which frame you want to start capturing at, use the capture trigger.
827 // Initialize it to the number of frames you want to capture, and then clear the value to 0 when
828 // you reach the content you want to capture. Currently only available on Android.
829 uint32_t mCaptureTrigger;
830
831 bool mCaptureActive;
832 std::vector<uint32_t> mActiveFrameIndices;
833
834 // Cache most recently compiled and linked sources.
835 ShaderSourceMap mCachedShaderSource;
836 ProgramSourceMap mCachedProgramSources;
837
838 // Set of programs which were created but not linked before capture was started
839 std::set<gl::ShaderProgramID> mDeferredLinkPrograms;
840
841 gl::ContextID mWindowSurfaceContextID;
842
843 std::vector<CallCapture> mShareGroupSetupCalls;
844 // Track which Contexts were created and made current at least once before MEC,
845 // requiring setup for replay
846 std::unordered_set<GLuint> mActiveContexts;
847
848 // Invalid call counts per entry point while capture is active and inactive.
849 std::unordered_map<EntryPoint, size_t> mInvalidCallCountsActive;
850 std::unordered_map<EntryPoint, size_t> mInvalidCallCountsInactive;
851 };
852
853 template <typename CaptureFuncT, typename... ArgsT>
CaptureGLCallToFrameCapture(CaptureFuncT captureFunc,bool isCallValid,gl::Context * context,ArgsT...captureParams)854 void CaptureGLCallToFrameCapture(CaptureFuncT captureFunc,
855 bool isCallValid,
856 gl::Context *context,
857 ArgsT... captureParams)
858 {
859 FrameCaptureShared *frameCaptureShared = context->getShareGroup()->getFrameCaptureShared();
860
861 // EGL calls are protected by the global context mutex but only a subset of GL calls
862 // are so protected. Ensure FrameCaptureShared access thread safety by using a
863 // frame-capture only mutex.
864 std::lock_guard<angle::SimpleMutex> lock(frameCaptureShared->getFrameCaptureMutex());
865
866 if (!frameCaptureShared->isCapturing())
867 {
868 return;
869 }
870
871 CallCapture call = captureFunc(context->getState(), isCallValid, captureParams...);
872 frameCaptureShared->captureCall(context, std::move(call), isCallValid);
873 }
874
875 template <typename FirstT, typename... OthersT>
GetEGLDisplayArg(FirstT display,OthersT...others)876 egl::Display *GetEGLDisplayArg(FirstT display, OthersT... others)
877 {
878 if constexpr (std::is_same<egl::Display *, FirstT>::value)
879 {
880 return display;
881 }
882 return nullptr;
883 }
884
885 template <typename CaptureFuncT, typename... ArgsT>
CaptureEGLCallToFrameCapture(CaptureFuncT captureFunc,bool isCallValid,egl::Thread * thread,ArgsT...captureParams)886 void CaptureEGLCallToFrameCapture(CaptureFuncT captureFunc,
887 bool isCallValid,
888 egl::Thread *thread,
889 ArgsT... captureParams)
890 {
891 gl::Context *context = thread->getContext();
892 if (!context)
893 {
894 // Get a valid context from the display argument if no context is associated with this
895 // thread
896 egl::Display *display = GetEGLDisplayArg(captureParams...);
897 if (display)
898 {
899 for (const auto &contextIter : display->getState().contextMap)
900 {
901 context = contextIter.second;
902 break;
903 }
904 }
905 if (!context)
906 {
907 return;
908 }
909 }
910 std::lock_guard<egl::ContextMutex> lock(context->getContextMutex());
911
912 angle::FrameCaptureShared *frameCaptureShared =
913 context->getShareGroup()->getFrameCaptureShared();
914 if (!frameCaptureShared->isCapturing())
915 {
916 return;
917 }
918
919 angle::CallCapture call = captureFunc(thread, isCallValid, captureParams...);
920 frameCaptureShared->captureCall(context, std::move(call), true);
921 }
922
923 // Pointer capture helpers.
924 void CaptureMemory(const void *source, size_t size, ParamCapture *paramCapture);
925 void CaptureString(const GLchar *str, ParamCapture *paramCapture);
926 void CaptureStringLimit(const GLchar *str, uint32_t limit, ParamCapture *paramCapture);
927 void CaptureVertexPointerGLES1(const gl::State &glState,
928 gl::ClientVertexArrayType type,
929 const void *pointer,
930 ParamCapture *paramCapture);
931
932 gl::Program *GetProgramForCapture(const gl::State &glState, gl::ShaderProgramID handle);
933
934 // For GetIntegerv, GetFloatv, etc.
935 void CaptureGetParameter(const gl::State &glState,
936 GLenum pname,
937 size_t typeSize,
938 ParamCapture *paramCapture);
939
940 void CaptureGetActiveUniformBlockivParameters(const gl::State &glState,
941 gl::ShaderProgramID handle,
942 gl::UniformBlockIndex uniformBlockIndex,
943 GLenum pname,
944 ParamCapture *paramCapture);
945
946 template <typename T>
CaptureClearBufferValue(GLenum buffer,const T * value,ParamCapture * paramCapture)947 void CaptureClearBufferValue(GLenum buffer, const T *value, ParamCapture *paramCapture)
948 {
949 // Per the spec, color buffers have a vec4, the rest a single value
950 uint32_t valueSize = (buffer == GL_COLOR) ? 4 : 1;
951 CaptureMemory(value, valueSize * sizeof(T), paramCapture);
952 }
953
954 void CaptureGenHandlesImpl(GLsizei n, GLuint *handles, ParamCapture *paramCapture);
955
956 template <typename T>
CaptureGenHandles(GLsizei n,T * handles,ParamCapture * paramCapture)957 void CaptureGenHandles(GLsizei n, T *handles, ParamCapture *paramCapture)
958 {
959 paramCapture->dataNElements = n;
960 CaptureGenHandlesImpl(n, reinterpret_cast<GLuint *>(handles), paramCapture);
961 }
962
963 template <typename T>
CaptureArray(T * elements,GLsizei n,ParamCapture * paramCapture)964 void CaptureArray(T *elements, GLsizei n, ParamCapture *paramCapture)
965 {
966 paramCapture->dataNElements = n;
967 CaptureMemory(elements, n * sizeof(T), paramCapture);
968 }
969
970 void CaptureShaderStrings(GLsizei count,
971 const GLchar *const *strings,
972 const GLint *length,
973 ParamCapture *paramCapture);
974
975 bool IsTrackedPerContext(ResourceIDType type);
976 } // namespace angle
977
978 template <typename T>
CaptureTextureAndSamplerParameter_params(GLenum pname,const T * param,angle::ParamCapture * paramCapture)979 void CaptureTextureAndSamplerParameter_params(GLenum pname,
980 const T *param,
981 angle::ParamCapture *paramCapture)
982 {
983 if (pname == GL_TEXTURE_BORDER_COLOR || pname == GL_TEXTURE_CROP_RECT_OES)
984 {
985 CaptureMemory(param, sizeof(T) * 4, paramCapture);
986 }
987 else
988 {
989 CaptureMemory(param, sizeof(T), paramCapture);
990 }
991 }
992
993 namespace egl
994 {
995 angle::ParamCapture CaptureAttributeMap(const egl::AttributeMap &attribMap);
996 } // namespace egl
997
998 #endif // LIBANGLE_FRAME_CAPTURE_H_
999