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