| // |
| // Copyright 2019 The ANGLE Project Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| // |
| // FrameCapture.h: |
| // ANGLE Frame capture interface. |
| // |
| |
| #ifndef LIBANGLE_FRAME_CAPTURE_H_ |
| #define LIBANGLE_FRAME_CAPTURE_H_ |
| |
| #include "common/PackedEnums.h" |
| #include "common/system_utils.h" |
| #include "libANGLE/Context.h" |
| #include "libANGLE/angletypes.h" |
| #include "libANGLE/capture/frame_capture_utils_autogen.h" |
| #include "libANGLE/entry_points_utils.h" |
| |
| namespace gl |
| { |
| enum class BigGLEnum; |
| enum class GLESEnum; |
| } // namespace gl |
| |
| namespace angle |
| { |
| |
| using ParamData = std::vector<std::vector<uint8_t>>; |
| struct ParamCapture : angle::NonCopyable |
| { |
| ParamCapture(); |
| ParamCapture(const char *nameIn, ParamType typeIn); |
| ~ParamCapture(); |
| |
| ParamCapture(ParamCapture &&other); |
| ParamCapture &operator=(ParamCapture &&other); |
| |
| std::string name; |
| ParamType type; |
| ParamValue value; |
| gl::GLESEnum enumGroup; // only used for param type GLenum, GLboolean and GLbitfield |
| gl::BigGLEnum bigGLEnum; // only used for param type GLenum, GLboolean and GLbitfield |
| ParamData data; |
| int dataNElements = 0; |
| int arrayClientPointerIndex = -1; |
| size_t readBufferSizeBytes = 0; |
| }; |
| |
| class ParamBuffer final : angle::NonCopyable |
| { |
| public: |
| ParamBuffer(); |
| ~ParamBuffer(); |
| |
| ParamBuffer(ParamBuffer &&other); |
| ParamBuffer &operator=(ParamBuffer &&other); |
| |
| template <typename T> |
| void addValueParam(const char *paramName, ParamType paramType, T paramValue); |
| template <typename T> |
| void setValueParamAtIndex(const char *paramName, ParamType paramType, T paramValue, int index); |
| template <typename T> |
| void addEnumParam(const char *paramName, |
| gl::GLESEnum enumGroup, |
| ParamType paramType, |
| T paramValue); |
| template <typename T> |
| void addEnumParam(const char *paramName, |
| gl::BigGLEnum enumGroup, |
| ParamType paramType, |
| T paramValue); |
| |
| ParamCapture &getParam(const char *paramName, ParamType paramType, int index); |
| const ParamCapture &getParam(const char *paramName, ParamType paramType, int index) const; |
| ParamCapture &getParamFlexName(const char *paramName1, |
| const char *paramName2, |
| ParamType paramType, |
| int index); |
| const ParamCapture &getParamFlexName(const char *paramName1, |
| const char *paramName2, |
| ParamType paramType, |
| int index) const; |
| const ParamCapture &getReturnValue() const { return mReturnValueCapture; } |
| |
| void addParam(ParamCapture &¶m); |
| void addReturnValue(ParamCapture &&returnValue); |
| bool hasClientArrayData() const { return mClientArrayDataParam != -1; } |
| ParamCapture &getClientArrayPointerParameter(); |
| size_t getReadBufferSize() const { return mReadBufferSize; } |
| |
| const std::vector<ParamCapture> &getParamCaptures() const { return mParamCaptures; } |
| |
| // These helpers allow us to track the ID of the buffer that was active when |
| // MapBufferRange was called. We'll use it during replay to track the |
| // buffer's contents, as they can be modified by the host. |
| void setMappedBufferID(gl::BufferID bufferID) { mMappedBufferID = bufferID; } |
| gl::BufferID getMappedBufferID() const { return mMappedBufferID; } |
| |
| private: |
| std::vector<ParamCapture> mParamCaptures; |
| ParamCapture mReturnValueCapture; |
| int mClientArrayDataParam = -1; |
| size_t mReadBufferSize = 0; |
| gl::BufferID mMappedBufferID; |
| }; |
| |
| struct CallCapture |
| { |
| CallCapture(EntryPoint entryPointIn, ParamBuffer &¶msIn); |
| CallCapture(const std::string &customFunctionNameIn, ParamBuffer &¶msIn); |
| ~CallCapture(); |
| |
| CallCapture(CallCapture &&other); |
| CallCapture &operator=(CallCapture &&other); |
| |
| const char *name() const; |
| |
| EntryPoint entryPoint; |
| std::string customFunctionName; |
| ParamBuffer params; |
| bool isActive = true; |
| }; |
| |
| class ReplayContext |
| { |
| public: |
| ReplayContext(size_t readBufferSizebytes, const gl::AttribArray<size_t> &clientArraysSizebytes); |
| ~ReplayContext(); |
| |
| template <typename T> |
| T getReadBufferPointer(const ParamCapture ¶m) |
| { |
| ASSERT(param.readBufferSizeBytes > 0); |
| ASSERT(mReadBuffer.size() >= param.readBufferSizeBytes); |
| return reinterpret_cast<T>(mReadBuffer.data()); |
| } |
| template <typename T> |
| T getAsConstPointer(const ParamCapture ¶m) |
| { |
| if (param.arrayClientPointerIndex != -1) |
| { |
| return reinterpret_cast<T>(mClientArraysBuffer[param.arrayClientPointerIndex].data()); |
| } |
| |
| if (!param.data.empty()) |
| { |
| ASSERT(param.data.size() == 1); |
| return reinterpret_cast<T>(param.data[0].data()); |
| } |
| |
| return nullptr; |
| } |
| |
| template <typename T> |
| T getAsPointerConstPointer(const ParamCapture ¶m) |
| { |
| static_assert(sizeof(typename std::remove_pointer<T>::type) == sizeof(uint8_t *), |
| "pointer size not match!"); |
| |
| ASSERT(!param.data.empty()); |
| mPointersBuffer.clear(); |
| mPointersBuffer.reserve(param.data.size()); |
| for (const std::vector<uint8_t> &data : param.data) |
| { |
| mPointersBuffer.emplace_back(data.data()); |
| } |
| return reinterpret_cast<T>(mPointersBuffer.data()); |
| } |
| |
| gl::AttribArray<std::vector<uint8_t>> &getClientArraysBuffer() { return mClientArraysBuffer; } |
| |
| private: |
| std::vector<uint8_t> mReadBuffer; |
| std::vector<const uint8_t *> mPointersBuffer; |
| gl::AttribArray<std::vector<uint8_t>> mClientArraysBuffer; |
| }; |
| |
| // Helper to use unique IDs for each local data variable. |
| class DataCounters final : angle::NonCopyable |
| { |
| public: |
| DataCounters(); |
| ~DataCounters(); |
| |
| int getAndIncrement(EntryPoint entryPoint, const std::string ¶mName); |
| |
| private: |
| // <CallName, ParamName> |
| using Counter = std::pair<EntryPoint, std::string>; |
| std::map<Counter, int> mData; |
| }; |
| |
| constexpr int kStringsNotFound = -1; |
| class StringCounters final : angle::NonCopyable |
| { |
| public: |
| StringCounters(); |
| ~StringCounters(); |
| |
| int getStringCounter(const std::vector<std::string> &str); |
| void setStringCounter(const std::vector<std::string> &str, int &counter); |
| |
| private: |
| std::map<std::vector<std::string>, int> mStringCounterMap; |
| }; |
| |
| class DataTracker final : angle::NonCopyable |
| { |
| public: |
| DataTracker(); |
| ~DataTracker(); |
| |
| DataCounters &getCounters() { return mCounters; } |
| StringCounters &getStringCounters() { return mStringCounters; } |
| |
| private: |
| DataCounters mCounters; |
| StringCounters mStringCounters; |
| }; |
| |
| class ReplayWriter final : angle::NonCopyable |
| { |
| public: |
| ReplayWriter(); |
| ~ReplayWriter(); |
| |
| void setSourceFileSizeThreshold(size_t sourceFileSizeThreshold); |
| void setFilenamePattern(const std::string &pattern); |
| void setCaptureLabel(const std::string &label); |
| void setSourcePrologue(const std::string &prologue); |
| void setHeaderPrologue(const std::string &prologue); |
| |
| void addPublicFunction(const std::string &functionProto, |
| const std::stringstream &headerStream, |
| const std::stringstream &bodyStream); |
| void addPrivateFunction(const std::string &functionProto, |
| const std::stringstream &headerStream, |
| const std::stringstream &bodyStream); |
| std::string getInlineVariableName(EntryPoint entryPoint, const std::string ¶mName); |
| |
| std::string getInlineStringSetVariableName(EntryPoint entryPoint, |
| const std::string ¶mName, |
| const std::vector<std::string> &strings, |
| bool *isNewEntryOut); |
| |
| void saveFrame(); |
| void saveFrameIfFull(); |
| void saveIndexFilesAndHeader(); |
| void saveSetupFile(); |
| |
| std::vector<std::string> getAndResetWrittenFiles(); |
| |
| private: |
| static std::string GetVarName(EntryPoint entryPoint, const std::string ¶mName, int counter); |
| |
| void saveHeader(); |
| void writeReplaySource(const std::string &filename); |
| void addWrittenFile(const std::string &filename); |
| size_t getStoredReplaySourceSize() const; |
| |
| size_t mSourceFileSizeThreshold; |
| size_t mFrameIndex; |
| |
| DataTracker mDataTracker; |
| std::string mFilenamePattern; |
| std::string mCaptureLabel; |
| std::string mSourcePrologue; |
| std::string mHeaderPrologue; |
| |
| std::vector<std::string> mReplayHeaders; |
| std::vector<std::string> mGlobalVariableDeclarations; |
| |
| std::vector<std::string> mPublicFunctionPrototypes; |
| std::vector<std::string> mPublicFunctions; |
| |
| std::vector<std::string> mPrivateFunctionPrototypes; |
| std::vector<std::string> mPrivateFunctions; |
| |
| std::vector<std::string> mWrittenFiles; |
| }; |
| |
| using BufferCalls = std::map<GLuint, std::vector<CallCapture>>; |
| |
| // true means mapped, false means unmapped |
| using BufferMapStatusMap = std::map<GLuint, bool>; |
| |
| using FenceSyncSet = std::set<GLsync>; |
| using FenceSyncCalls = std::map<GLsync, std::vector<CallCapture>>; |
| |
| // For default uniforms, we need to track which ones are dirty, and the series of calls to reset. |
| // Each program has unique default uniforms, and each uniform has one or more locations in the |
| // default buffer. For reset efficiency, we track only the uniforms dirty by location, per program. |
| |
| // A set of all default uniforms (per program) that were modified during the run |
| using DefaultUniformLocationsSet = std::set<gl::UniformLocation>; |
| using DefaultUniformLocationsPerProgramMap = |
| std::map<gl::ShaderProgramID, DefaultUniformLocationsSet>; |
| |
| // A map of programs which maps to locations and their reset calls |
| using DefaultUniformCallsPerLocationMap = std::map<gl::UniformLocation, std::vector<CallCapture>>; |
| using DefaultUniformCallsPerProgramMap = |
| std::map<gl::ShaderProgramID, DefaultUniformCallsPerLocationMap>; |
| |
| using DefaultUniformBaseLocationMap = |
| std::map<std::pair<gl::ShaderProgramID, gl::UniformLocation>, gl::UniformLocation>; |
| |
| using ResourceSet = std::set<GLuint>; |
| using ResourceCalls = std::map<GLuint, std::vector<CallCapture>>; |
| |
| class TrackedResource final : angle::NonCopyable |
| { |
| public: |
| TrackedResource(); |
| ~TrackedResource(); |
| |
| const ResourceSet &getStartingResources() const { return mStartingResources; } |
| ResourceSet &getStartingResources() { return mStartingResources; } |
| const ResourceSet &getNewResources() const { return mNewResources; } |
| ResourceSet &getNewResources() { return mNewResources; } |
| const ResourceSet &getResourcesToRegen() const { return mResourcesToRegen; } |
| ResourceSet &getResourcesToRegen() { return mResourcesToRegen; } |
| const ResourceSet &getResourcesToRestore() const { return mResourcesToRestore; } |
| ResourceSet &getResourcesToRestore() { return mResourcesToRestore; } |
| |
| void setGennedResource(GLuint id); |
| void setDeletedResource(GLuint id); |
| void setModifiedResource(GLuint id); |
| bool resourceIsGenerated(GLuint id); |
| |
| ResourceCalls &getResourceRegenCalls() { return mResourceRegenCalls; } |
| ResourceCalls &getResourceRestoreCalls() { return mResourceRestoreCalls; } |
| |
| private: |
| // Resource regen calls will gen a resource |
| ResourceCalls mResourceRegenCalls; |
| // Resource restore calls will restore the contents of a resource |
| ResourceCalls mResourceRestoreCalls; |
| |
| // Resources created during startup |
| ResourceSet mStartingResources; |
| |
| // Resources created during the run that need to be deleted |
| ResourceSet mNewResources; |
| // Resources deleted during the run that need to be recreated |
| ResourceSet mResourcesToRegen; |
| // Resources modified during the run that need to be restored |
| ResourceSet mResourcesToRestore; |
| }; |
| |
| using TrackedResourceArray = |
| std::array<TrackedResource, static_cast<uint32_t>(ResourceIDType::EnumCount)>; |
| |
| // Helper to track resource changes during the capture |
| class ResourceTracker final : angle::NonCopyable |
| { |
| public: |
| ResourceTracker(); |
| ~ResourceTracker(); |
| |
| BufferCalls &getBufferMapCalls() { return mBufferMapCalls; } |
| BufferCalls &getBufferUnmapCalls() { return mBufferUnmapCalls; } |
| |
| std::vector<CallCapture> &getBufferBindingCalls() { return mBufferBindingCalls; } |
| |
| void setBufferMapped(gl::ContextID contextID, GLuint id); |
| void setBufferUnmapped(gl::ContextID contextID, GLuint id); |
| |
| bool getStartingBuffersMappedCurrent(GLuint id) const; |
| bool getStartingBuffersMappedInitial(GLuint id) const; |
| |
| void setStartingBufferMapped(GLuint id, bool mapped) |
| { |
| // Track the current state (which will change throughout the trace) |
| mStartingBuffersMappedCurrent[id] = mapped; |
| |
| // And the initial state, to compare during frame loop reset |
| mStartingBuffersMappedInitial[id] = mapped; |
| } |
| |
| void onShaderProgramAccess(gl::ShaderProgramID shaderProgramID); |
| uint32_t getMaxShaderPrograms() const { return mMaxShaderPrograms; } |
| |
| FenceSyncSet &getStartingFenceSyncs() { return mStartingFenceSyncs; } |
| FenceSyncCalls &getFenceSyncRegenCalls() { return mFenceSyncRegenCalls; } |
| FenceSyncSet &getFenceSyncsToRegen() { return mFenceSyncsToRegen; } |
| void setDeletedFenceSync(GLsync sync); |
| |
| DefaultUniformLocationsPerProgramMap &getDefaultUniformsToReset() |
| { |
| return mDefaultUniformsToReset; |
| } |
| DefaultUniformCallsPerLocationMap &getDefaultUniformResetCalls(gl::ShaderProgramID id) |
| { |
| return mDefaultUniformResetCalls[id]; |
| } |
| void setModifiedDefaultUniform(gl::ShaderProgramID programID, gl::UniformLocation location); |
| void setDefaultUniformBaseLocation(gl::ShaderProgramID programID, |
| gl::UniformLocation location, |
| gl::UniformLocation baseLocation); |
| gl::UniformLocation getDefaultUniformBaseLocation(gl::ShaderProgramID programID, |
| gl::UniformLocation location) |
| { |
| ASSERT(mDefaultUniformBaseLocations.find({programID, location}) != |
| mDefaultUniformBaseLocations.end()); |
| return mDefaultUniformBaseLocations[{programID, location}]; |
| } |
| |
| TrackedResource &getTrackedResource(gl::ContextID contextID, ResourceIDType type); |
| |
| void getContextIDs(std::set<gl::ContextID> &idsOut); |
| |
| std::map<void *, egl::AttributeMap> &getImageToAttribTable() { return mMatchImageToAttribs; } |
| |
| std::map<GLuint, void *> &getTextureIDToImageTable() { return mMatchTextureIDToImage; } |
| |
| private: |
| // Buffer map calls will map a buffer with correct offset, length, and access flags |
| BufferCalls mBufferMapCalls; |
| // Buffer unmap calls will bind and unmap a given buffer |
| BufferCalls mBufferUnmapCalls; |
| |
| // Buffer binding calls to restore bindings recorded during MEC |
| std::vector<CallCapture> mBufferBindingCalls; |
| |
| // Whether a given buffer was mapped at the start of the trace |
| BufferMapStatusMap mStartingBuffersMappedInitial; |
| // The status of buffer mapping throughout the trace, modified with each Map/Unmap call |
| BufferMapStatusMap mStartingBuffersMappedCurrent; |
| |
| // Maximum accessed shader program ID. |
| uint32_t mMaxShaderPrograms = 0; |
| |
| // Fence sync objects created during MEC setup |
| FenceSyncSet mStartingFenceSyncs; |
| // Fence sync regen calls will create a fence sync objects |
| FenceSyncCalls mFenceSyncRegenCalls; |
| // Fence syncs to regen are a list of starting fence sync objects that were deleted and need to |
| // be regen'ed. |
| FenceSyncSet mFenceSyncsToRegen; |
| |
| // Default uniforms that were modified during the run |
| DefaultUniformLocationsPerProgramMap mDefaultUniformsToReset; |
| // Calls per default uniform to return to original state |
| DefaultUniformCallsPerProgramMap mDefaultUniformResetCalls; |
| |
| // Base location of arrayed uniforms |
| DefaultUniformBaseLocationMap mDefaultUniformBaseLocations; |
| |
| // Tracked resources per context |
| TrackedResourceArray mTrackedResourcesShared; |
| std::map<gl::ContextID, TrackedResourceArray> mTrackedResourcesPerContext; |
| |
| std::map<void *, egl::AttributeMap> mMatchImageToAttribs; |
| std::map<GLuint, void *> mMatchTextureIDToImage; |
| }; |
| |
| // Used by the CPP replay to filter out unnecessary code. |
| using HasResourceTypeMap = angle::PackedEnumBitSet<ResourceIDType>; |
| |
| // Map of ResourceType to IDs and range of setup calls |
| using ResourceIDToSetupCallsMap = |
| PackedEnumMap<ResourceIDType, std::map<GLuint, gl::Range<size_t>>>; |
| |
| // Map of buffer ID to offset and size used when mapped |
| using BufferDataMap = std::map<gl::BufferID, std::pair<GLintptr, GLsizeiptr>>; |
| |
| // A dictionary of sources indexed by shader type. |
| using ProgramSources = gl::ShaderMap<std::string>; |
| |
| // Maps from IDs to sources. |
| using ShaderSourceMap = std::map<gl::ShaderProgramID, std::string>; |
| using ProgramSourceMap = std::map<gl::ShaderProgramID, ProgramSources>; |
| |
| // Map from textureID to level and data |
| using TextureLevels = std::map<GLint, std::vector<uint8_t>>; |
| using TextureLevelDataMap = std::map<gl::TextureID, TextureLevels>; |
| |
| struct SurfaceParams |
| { |
| gl::Extents extents; |
| egl::ColorSpace colorSpace; |
| }; |
| |
| // Map from ContextID to SurfaceParams |
| using SurfaceParamsMap = std::map<gl::ContextID, SurfaceParams>; |
| |
| using CallVector = std::vector<std::vector<CallCapture> *>; |
| |
| // A map from API entry point to calls |
| using CallResetMap = std::map<angle::EntryPoint, std::vector<CallCapture>>; |
| |
| // StateResetHelper provides a simple way to track whether an entry point has been called during the |
| // trace, along with the reset calls to get it back to starting state. This is useful for things |
| // that are one dimensional, like context bindings or context state. |
| class StateResetHelper final : angle::NonCopyable |
| { |
| public: |
| StateResetHelper(); |
| ~StateResetHelper(); |
| |
| const std::set<angle::EntryPoint> &getDirtyEntryPoints() const { return mDirtyEntryPoints; } |
| void setEntryPointDirty(EntryPoint entryPoint) { mDirtyEntryPoints.insert(entryPoint); } |
| |
| CallResetMap &getResetCalls() { return mResetCalls; } |
| const CallResetMap &getResetCalls() const { return mResetCalls; } |
| |
| void setDefaultResetCalls(const gl::Context *context, angle::EntryPoint); |
| |
| private: |
| // Dirty state per entry point |
| std::set<angle::EntryPoint> mDirtyEntryPoints; |
| |
| // Reset calls per API entry point |
| CallResetMap mResetCalls; |
| }; |
| |
| class FrameCapture final : angle::NonCopyable |
| { |
| public: |
| FrameCapture(); |
| ~FrameCapture(); |
| |
| std::vector<CallCapture> &getSetupCalls() { return mSetupCalls; } |
| void clearSetupCalls() { mSetupCalls.clear(); } |
| |
| StateResetHelper &getStateResetHelper() { return mStateResetHelper; } |
| |
| void reset(); |
| |
| private: |
| std::vector<CallCapture> mSetupCalls; |
| |
| StateResetHelper mStateResetHelper; |
| }; |
| |
| // Page range inside a coherent buffer |
| struct PageRange |
| { |
| PageRange(size_t start, size_t end); |
| ~PageRange(); |
| |
| // Relative start page |
| size_t start; |
| |
| // First page after the relative end |
| size_t end; |
| }; |
| |
| // Memory address range defined by start and size |
| struct AddressRange |
| { |
| AddressRange(); |
| AddressRange(uintptr_t start, size_t size); |
| ~AddressRange(); |
| |
| uintptr_t end(); |
| |
| uintptr_t start; |
| size_t size; |
| }; |
| |
| // Used to handle protection of buffers that overlap in pages. |
| enum class PageSharingType |
| { |
| NoneShared, |
| FirstShared, |
| LastShared, |
| FirstAndLastShared |
| }; |
| |
| class CoherentBuffer |
| { |
| public: |
| CoherentBuffer(uintptr_t start, size_t size, size_t pageSize); |
| ~CoherentBuffer(); |
| |
| // Sets the a range in the buffer clean and protects a selected range |
| void protectPageRange(const PageRange &pageRange); |
| |
| // Sets a page dirty state and sets it's protection |
| void setDirty(size_t relativePage, bool dirty); |
| |
| // Removes protection |
| void removeProtection(PageSharingType sharingType); |
| |
| bool contains(size_t page, size_t *relativePage); |
| bool isDirty(); |
| |
| // Returns dirty page ranges |
| std::vector<PageRange> getDirtyPageRanges(); |
| |
| // Calculates address range from page range |
| AddressRange getDirtyAddressRange(const PageRange &dirtyPageRange); |
| AddressRange getRange(); |
| |
| private: |
| // Actual buffer start and size |
| AddressRange mRange; |
| |
| // Start and size of page aligned protected area |
| AddressRange mProtectionRange; |
| |
| // Start and end of protection in relative pages, calculated from mProtectionRange. |
| size_t mProtectionStartPage; |
| size_t mProtectionEndPage; |
| |
| size_t mPageCount; |
| size_t mPageSize; |
| |
| // Clean pages are protected |
| std::vector<bool> mDirtyPages; |
| }; |
| |
| class CoherentBufferTracker final : angle::NonCopyable |
| { |
| public: |
| CoherentBufferTracker(); |
| ~CoherentBufferTracker(); |
| |
| bool isDirty(gl::BufferID id); |
| void addBuffer(gl::BufferID id, uintptr_t start, size_t size); |
| void removeBuffer(gl::BufferID id); |
| void disable(); |
| void enable(); |
| void onEndFrame(); |
| |
| private: |
| // Detect overlapping pages when removing protection |
| PageSharingType doesBufferSharePage(gl::BufferID id); |
| |
| // Returns a map to found buffers and the corresponding pages for a given address. |
| // For addresses that are in a page shared by 2 buffers, 2 results are returned. |
| HashMap<std::shared_ptr<CoherentBuffer>, size_t> getBufferPagesForAddress(uintptr_t address); |
| PageFaultHandlerRangeType handleWrite(uintptr_t address); |
| bool haveBuffer(gl::BufferID id); |
| |
| public: |
| std::mutex mMutex; |
| HashMap<GLuint, std::shared_ptr<CoherentBuffer>> mBuffers; |
| |
| private: |
| bool mEnabled = false; |
| std::unique_ptr<PageFaultHandler> mPageFaultHandler; |
| size_t mPageSize; |
| }; |
| |
| // Shared class for any items that need to be tracked by FrameCapture across shared contexts |
| class FrameCaptureShared final : angle::NonCopyable |
| { |
| public: |
| FrameCaptureShared(); |
| ~FrameCaptureShared(); |
| |
| void captureCall(const gl::Context *context, CallCapture &&call, bool isCallValid); |
| void checkForCaptureTrigger(); |
| void onEndFrame(const gl::Context *context); |
| void onDestroyContext(const gl::Context *context); |
| void onMakeCurrent(const gl::Context *context, const egl::Surface *drawSurface); |
| bool enabled() const { return mEnabled; } |
| |
| bool isCapturing() const; |
| void replay(gl::Context *context); |
| uint32_t getFrameCount() const; |
| |
| // Returns a frame index starting from "1" as the first frame. |
| uint32_t getReplayFrameIndex() const; |
| |
| void trackBufferMapping(const gl::Context *context, |
| CallCapture *call, |
| gl::BufferID id, |
| gl::Buffer *buffer, |
| GLintptr offset, |
| GLsizeiptr length, |
| bool writable, |
| bool coherent); |
| |
| void trackTextureUpdate(const gl::Context *context, const CallCapture &call); |
| void trackDefaultUniformUpdate(const gl::Context *context, const CallCapture &call); |
| void trackVertexArrayUpdate(const gl::Context *context, const CallCapture &call); |
| |
| const std::string &getShaderSource(gl::ShaderProgramID id) const; |
| void setShaderSource(gl::ShaderProgramID id, std::string sources); |
| |
| const ProgramSources &getProgramSources(gl::ShaderProgramID id) const; |
| void setProgramSources(gl::ShaderProgramID id, ProgramSources sources); |
| |
| // Load data from a previously stored texture level |
| const std::vector<uint8_t> &retrieveCachedTextureLevel(gl::TextureID id, |
| gl::TextureTarget target, |
| GLint level); |
| |
| // Create new texture level data and copy the source into it |
| void copyCachedTextureLevel(const gl::Context *context, |
| gl::TextureID srcID, |
| GLint srcLevel, |
| gl::TextureID dstID, |
| GLint dstLevel, |
| const CallCapture &call); |
| |
| // Create the location that should be used to cache texture level data |
| std::vector<uint8_t> &getCachedTextureLevelData(gl::Texture *texture, |
| gl::TextureTarget target, |
| GLint level, |
| EntryPoint entryPoint); |
| |
| // Capture coherent buffer storages |
| void captureCoherentBufferSnapshot(const gl::Context *context, gl::BufferID bufferID); |
| |
| // Remove any cached texture levels on deletion |
| void deleteCachedTextureLevelData(gl::TextureID id); |
| |
| void eraseBufferDataMapEntry(const gl::BufferID bufferId) |
| { |
| const auto &bufferDataInfo = mBufferDataMap.find(bufferId); |
| if (bufferDataInfo != mBufferDataMap.end()) |
| { |
| mBufferDataMap.erase(bufferDataInfo); |
| } |
| } |
| |
| bool hasBufferData(gl::BufferID bufferID) |
| { |
| const auto &bufferDataInfo = mBufferDataMap.find(bufferID); |
| if (bufferDataInfo != mBufferDataMap.end()) |
| { |
| return true; |
| } |
| return false; |
| } |
| |
| std::pair<GLintptr, GLsizeiptr> getBufferDataOffsetAndLength(gl::BufferID bufferID) |
| { |
| const auto &bufferDataInfo = mBufferDataMap.find(bufferID); |
| ASSERT(bufferDataInfo != mBufferDataMap.end()); |
| return bufferDataInfo->second; |
| } |
| |
| void setCaptureActive() { mCaptureActive = true; } |
| void setCaptureInactive() { mCaptureActive = false; } |
| bool isCaptureActive() { return mCaptureActive; } |
| bool usesMidExecutionCapture() { return mCaptureStartFrame > 1; } |
| |
| gl::ContextID getWindowSurfaceContextID() const { return mWindowSurfaceContextID; } |
| |
| void markResourceSetupCallsInactive(std::vector<CallCapture> *setupCalls, |
| ResourceIDType type, |
| GLuint id, |
| gl::Range<size_t> range); |
| |
| void updateReadBufferSize(size_t readBufferSize) |
| { |
| mReadBufferSize = std::max(mReadBufferSize, readBufferSize); |
| } |
| |
| template <typename ResourceType> |
| void handleGennedResource(const gl::Context *context, ResourceType resourceID) |
| { |
| if (isCaptureActive()) |
| { |
| ResourceIDType idType = GetResourceIDTypeFromType<ResourceType>::IDType; |
| TrackedResource &tracker = mResourceTracker.getTrackedResource(context->id(), idType); |
| tracker.setGennedResource(resourceID.value); |
| } |
| } |
| |
| template <typename ResourceType> |
| bool resourceIsGenerated(const gl::Context *context, ResourceType resourceID) |
| { |
| ResourceIDType idType = GetResourceIDTypeFromType<ResourceType>::IDType; |
| TrackedResource &tracker = mResourceTracker.getTrackedResource(context->id(), idType); |
| return tracker.resourceIsGenerated(resourceID.value); |
| } |
| |
| template <typename ResourceType> |
| void handleDeletedResource(const gl::Context *context, ResourceType resourceID) |
| { |
| if (isCaptureActive()) |
| { |
| ResourceIDType idType = GetResourceIDTypeFromType<ResourceType>::IDType; |
| TrackedResource &tracker = mResourceTracker.getTrackedResource(context->id(), idType); |
| tracker.setDeletedResource(resourceID.value); |
| } |
| } |
| |
| private: |
| void writeJSON(const gl::Context *context); |
| void writeCppReplayIndexFiles(const gl::Context *context, bool writeResetContextCall); |
| void writeMainContextCppReplay(const gl::Context *context, |
| const std::vector<CallCapture> &setupCalls, |
| StateResetHelper &StateResetHelper); |
| |
| void captureClientArraySnapshot(const gl::Context *context, |
| size_t vertexCount, |
| size_t instanceCount); |
| void captureMappedBufferSnapshot(const gl::Context *context, const CallCapture &call); |
| |
| void copyCompressedTextureData(const gl::Context *context, const CallCapture &call); |
| void captureCompressedTextureData(const gl::Context *context, const CallCapture &call); |
| |
| void reset(); |
| void maybeOverrideEntryPoint(const gl::Context *context, |
| CallCapture &call, |
| std::vector<CallCapture> &newCalls); |
| void maybeCapturePreCallUpdates(const gl::Context *context, |
| CallCapture &call, |
| std::vector<CallCapture> *shareGroupSetupCalls, |
| ResourceIDToSetupCallsMap *resourceIDToSetupCalls); |
| template <typename ParamValueType> |
| void maybeGenResourceOnBind(const gl::Context *context, CallCapture &call); |
| void maybeCapturePostCallUpdates(const gl::Context *context); |
| void maybeCaptureDrawArraysClientData(const gl::Context *context, |
| CallCapture &call, |
| size_t instanceCount); |
| void maybeCaptureDrawElementsClientData(const gl::Context *context, |
| CallCapture &call, |
| size_t instanceCount); |
| void maybeCaptureCoherentBuffers(const gl::Context *context); |
| void updateCopyImageSubData(CallCapture &call); |
| void overrideProgramBinary(const gl::Context *context, |
| CallCapture &call, |
| std::vector<CallCapture> &outCalls); |
| void updateResourceCountsFromParamCapture(const ParamCapture ¶m, ResourceIDType idType); |
| void updateResourceCountsFromCallCapture(const CallCapture &call); |
| |
| void runMidExecutionCapture(const gl::Context *context); |
| |
| void scanSetupCalls(const gl::Context *context, std::vector<CallCapture> &setupCalls); |
| |
| static void ReplayCall(gl::Context *context, |
| ReplayContext *replayContext, |
| const CallCapture &call); |
| |
| std::vector<CallCapture> mFrameCalls; |
| gl::ContextID mLastContextId; |
| |
| // We save one large buffer of binary data for the whole CPP replay. |
| // This simplifies a lot of file management. |
| std::vector<uint8_t> mBinaryData; |
| |
| bool mEnabled; |
| bool mSerializeStateEnabled; |
| std::string mOutDirectory; |
| std::string mCaptureLabel; |
| bool mCompression; |
| gl::AttribArray<int> mClientVertexArrayMap; |
| uint32_t mFrameIndex; |
| uint32_t mCaptureStartFrame; |
| uint32_t mCaptureEndFrame; |
| bool mIsFirstFrame = true; |
| bool mWroteIndexFile = false; |
| SurfaceParamsMap mDrawSurfaceParams; |
| gl::AttribArray<size_t> mClientArraySizes; |
| size_t mReadBufferSize; |
| HasResourceTypeMap mHasResourceType; |
| ResourceIDToSetupCallsMap mResourceIDToSetupCalls; |
| BufferDataMap mBufferDataMap; |
| bool mValidateSerializedState = false; |
| std::string mValidationExpression; |
| bool mTrimEnabled = true; |
| PackedEnumMap<ResourceIDType, uint32_t> mMaxAccessedResourceIDs; |
| CoherentBufferTracker mCoherentBufferTracker; |
| |
| ResourceTracker mResourceTracker; |
| ReplayWriter mReplayWriter; |
| |
| // If you don't know which frame you want to start capturing at, use the capture trigger. |
| // Initialize it to the number of frames you want to capture, and then clear the value to 0 when |
| // you reach the content you want to capture. Currently only available on Android. |
| uint32_t mCaptureTrigger; |
| |
| bool mCaptureActive; |
| std::vector<uint32_t> mActiveFrameIndices; |
| |
| // Cache most recently compiled and linked sources. |
| ShaderSourceMap mCachedShaderSource; |
| ProgramSourceMap mCachedProgramSources; |
| |
| gl::ContextID mWindowSurfaceContextID; |
| |
| std::vector<CallCapture> mShareGroupSetupCalls; |
| }; |
| |
| template <typename CaptureFuncT, typename... ArgsT> |
| void CaptureCallToFrameCapture(CaptureFuncT captureFunc, |
| bool isCallValid, |
| gl::Context *context, |
| ArgsT... captureParams) |
| { |
| FrameCaptureShared *frameCaptureShared = context->getShareGroup()->getFrameCaptureShared(); |
| if (!frameCaptureShared->isCapturing()) |
| { |
| return; |
| } |
| |
| CallCapture call = captureFunc(context->getState(), isCallValid, captureParams...); |
| |
| frameCaptureShared->captureCall(context, std::move(call), isCallValid); |
| } |
| |
| template <typename T> |
| void ParamBuffer::addValueParam(const char *paramName, ParamType paramType, T paramValue) |
| { |
| ParamCapture capture(paramName, paramType); |
| InitParamValue(paramType, paramValue, &capture.value); |
| mParamCaptures.emplace_back(std::move(capture)); |
| } |
| |
| template <typename T> |
| void ParamBuffer::setValueParamAtIndex(const char *paramName, |
| ParamType paramType, |
| T paramValue, |
| int index) |
| { |
| ASSERT(mParamCaptures.size() > static_cast<size_t>(index)); |
| |
| ParamCapture capture(paramName, paramType); |
| InitParamValue(paramType, paramValue, &capture.value); |
| mParamCaptures[index] = std::move(capture); |
| } |
| |
| template <typename T> |
| void ParamBuffer::addEnumParam(const char *paramName, |
| gl::GLESEnum enumGroup, |
| ParamType paramType, |
| T paramValue) |
| { |
| ParamCapture capture(paramName, paramType); |
| InitParamValue(paramType, paramValue, &capture.value); |
| capture.enumGroup = enumGroup; |
| mParamCaptures.emplace_back(std::move(capture)); |
| } |
| |
| template <typename T> |
| void ParamBuffer::addEnumParam(const char *paramName, |
| gl::BigGLEnum enumGroup, |
| ParamType paramType, |
| T paramValue) |
| { |
| ParamCapture capture(paramName, paramType); |
| InitParamValue(paramType, paramValue, &capture.value); |
| capture.bigGLEnum = enumGroup; |
| mParamCaptures.emplace_back(std::move(capture)); |
| } |
| |
| // Pointer capture helpers. |
| void CaptureMemory(const void *source, size_t size, ParamCapture *paramCapture); |
| void CaptureString(const GLchar *str, ParamCapture *paramCapture); |
| void CaptureStringLimit(const GLchar *str, uint32_t limit, ParamCapture *paramCapture); |
| void CaptureVertexPointerGLES1(const gl::State &glState, |
| gl::ClientVertexArrayType type, |
| const void *pointer, |
| ParamCapture *paramCapture); |
| |
| gl::Program *GetProgramForCapture(const gl::State &glState, gl::ShaderProgramID handle); |
| |
| // For GetIntegerv, GetFloatv, etc. |
| void CaptureGetParameter(const gl::State &glState, |
| GLenum pname, |
| size_t typeSize, |
| ParamCapture *paramCapture); |
| |
| void CaptureGetActiveUniformBlockivParameters(const gl::State &glState, |
| gl::ShaderProgramID handle, |
| gl::UniformBlockIndex uniformBlockIndex, |
| GLenum pname, |
| ParamCapture *paramCapture); |
| |
| template <typename T> |
| void CaptureClearBufferValue(GLenum buffer, const T *value, ParamCapture *paramCapture) |
| { |
| // Per the spec, color buffers have a vec4, the rest a single value |
| uint32_t valueSize = (buffer == GL_COLOR) ? 4 : 1; |
| CaptureMemory(value, valueSize * sizeof(T), paramCapture); |
| } |
| |
| void CaptureGenHandlesImpl(GLsizei n, GLuint *handles, ParamCapture *paramCapture); |
| |
| template <typename T> |
| void CaptureGenHandles(GLsizei n, T *handles, ParamCapture *paramCapture) |
| { |
| paramCapture->dataNElements = n; |
| CaptureGenHandlesImpl(n, reinterpret_cast<GLuint *>(handles), paramCapture); |
| } |
| |
| template <typename T> |
| void CaptureArray(T *elements, GLsizei n, ParamCapture *paramCapture) |
| { |
| paramCapture->dataNElements = n; |
| CaptureMemory(elements, n * sizeof(T), paramCapture); |
| } |
| |
| void CaptureShaderStrings(GLsizei count, |
| const GLchar *const *strings, |
| const GLint *length, |
| ParamCapture *paramCapture); |
| |
| template <ParamType ParamT, typename T> |
| void WriteParamValueReplay(std::ostream &os, const CallCapture &call, T value); |
| |
| template <> |
| void WriteParamValueReplay<ParamType::TGLboolean>(std::ostream &os, |
| const CallCapture &call, |
| GLboolean value); |
| |
| template <> |
| void WriteParamValueReplay<ParamType::TGLbooleanPointer>(std::ostream &os, |
| const CallCapture &call, |
| GLboolean *value); |
| |
| template <> |
| void WriteParamValueReplay<ParamType::TvoidConstPointer>(std::ostream &os, |
| const CallCapture &call, |
| const void *value); |
| |
| template <> |
| void WriteParamValueReplay<ParamType::TvoidPointer>(std::ostream &os, |
| const CallCapture &call, |
| void *value); |
| |
| template <> |
| void WriteParamValueReplay<ParamType::TGLfloatConstPointer>(std::ostream &os, |
| const CallCapture &call, |
| const GLfloat *value); |
| |
| template <> |
| void WriteParamValueReplay<ParamType::TGLintConstPointer>(std::ostream &os, |
| const CallCapture &call, |
| const GLint *value); |
| |
| template <> |
| void WriteParamValueReplay<ParamType::TGLsizeiPointer>(std::ostream &os, |
| const CallCapture &call, |
| GLsizei *value); |
| |
| template <> |
| void WriteParamValueReplay<ParamType::TGLuintConstPointer>(std::ostream &os, |
| const CallCapture &call, |
| const GLuint *value); |
| |
| template <> |
| void WriteParamValueReplay<ParamType::TGLDEBUGPROCKHR>(std::ostream &os, |
| const CallCapture &call, |
| GLDEBUGPROCKHR value); |
| |
| template <> |
| void WriteParamValueReplay<ParamType::TGLDEBUGPROC>(std::ostream &os, |
| const CallCapture &call, |
| GLDEBUGPROC value); |
| |
| template <> |
| void WriteParamValueReplay<ParamType::TBufferID>(std::ostream &os, |
| const CallCapture &call, |
| gl::BufferID value); |
| |
| template <> |
| void WriteParamValueReplay<ParamType::TFenceNVID>(std::ostream &os, |
| const CallCapture &call, |
| gl::FenceNVID value); |
| |
| template <> |
| void WriteParamValueReplay<ParamType::TFramebufferID>(std::ostream &os, |
| const CallCapture &call, |
| gl::FramebufferID value); |
| |
| template <> |
| void WriteParamValueReplay<ParamType::TMemoryObjectID>(std::ostream &os, |
| const CallCapture &call, |
| gl::MemoryObjectID value); |
| |
| template <> |
| void WriteParamValueReplay<ParamType::TProgramPipelineID>(std::ostream &os, |
| const CallCapture &call, |
| gl::ProgramPipelineID value); |
| |
| template <> |
| void WriteParamValueReplay<ParamType::TQueryID>(std::ostream &os, |
| const CallCapture &call, |
| gl::QueryID value); |
| |
| template <> |
| void WriteParamValueReplay<ParamType::TRenderbufferID>(std::ostream &os, |
| const CallCapture &call, |
| gl::RenderbufferID value); |
| |
| template <> |
| void WriteParamValueReplay<ParamType::TSamplerID>(std::ostream &os, |
| const CallCapture &call, |
| gl::SamplerID value); |
| |
| template <> |
| void WriteParamValueReplay<ParamType::TSemaphoreID>(std::ostream &os, |
| const CallCapture &call, |
| gl::SemaphoreID value); |
| |
| template <> |
| void WriteParamValueReplay<ParamType::TShaderProgramID>(std::ostream &os, |
| const CallCapture &call, |
| gl::ShaderProgramID value); |
| |
| template <> |
| void WriteParamValueReplay<ParamType::TTextureID>(std::ostream &os, |
| const CallCapture &call, |
| gl::TextureID value); |
| |
| template <> |
| void WriteParamValueReplay<ParamType::TTransformFeedbackID>(std::ostream &os, |
| const CallCapture &call, |
| gl::TransformFeedbackID value); |
| |
| template <> |
| void WriteParamValueReplay<ParamType::TVertexArrayID>(std::ostream &os, |
| const CallCapture &call, |
| gl::VertexArrayID value); |
| |
| template <> |
| void WriteParamValueReplay<ParamType::TUniformLocation>(std::ostream &os, |
| const CallCapture &call, |
| gl::UniformLocation value); |
| |
| template <> |
| void WriteParamValueReplay<ParamType::TUniformBlockIndex>(std::ostream &os, |
| const CallCapture &call, |
| gl::UniformBlockIndex value); |
| |
| template <> |
| void WriteParamValueReplay<ParamType::TGLsync>(std::ostream &os, |
| const CallCapture &call, |
| GLsync value); |
| |
| template <> |
| void WriteParamValueReplay<ParamType::TGLeglImageOES>(std::ostream &os, |
| const CallCapture &call, |
| GLeglImageOES value); |
| |
| template <> |
| void WriteParamValueReplay<ParamType::TGLubyte>(std::ostream &os, |
| const CallCapture &call, |
| GLubyte value); |
| |
| template <> |
| void WriteParamValueReplay<ParamType::TEGLContext>(std::ostream &os, |
| const CallCapture &call, |
| EGLContext value); |
| |
| template <> |
| void WriteParamValueReplay<ParamType::TEGLDisplay>(std::ostream &os, |
| const CallCapture &call, |
| EGLContext value); |
| |
| template <> |
| void WriteParamValueReplay<ParamType::TEGLSurface>(std::ostream &os, |
| const CallCapture &call, |
| EGLContext value); |
| |
| template <> |
| void WriteParamValueReplay<ParamType::TEGLDEBUGPROCKHR>(std::ostream &os, |
| const CallCapture &call, |
| EGLDEBUGPROCKHR value); |
| |
| template <> |
| void WriteParamValueReplay<ParamType::TEGLGetBlobFuncANDROID>(std::ostream &os, |
| const CallCapture &call, |
| EGLGetBlobFuncANDROID value); |
| |
| template <> |
| void WriteParamValueReplay<ParamType::TEGLSetBlobFuncANDROID>(std::ostream &os, |
| const CallCapture &call, |
| EGLSetBlobFuncANDROID value); |
| template <> |
| void WriteParamValueReplay<ParamType::TEGLClientBuffer>(std::ostream &os, |
| const CallCapture &call, |
| EGLClientBuffer value); |
| |
| template <> |
| void WriteParamValueReplay<ParamType::TEGLConfig>(std::ostream &os, |
| const CallCapture &call, |
| EGLConfig value); |
| |
| template <> |
| void WriteParamValueReplay<ParamType::TEGLSurface>(std::ostream &os, |
| const CallCapture &call, |
| EGLSurface value); |
| |
| // General fallback for any unspecific type. |
| template <ParamType ParamT, typename T> |
| void WriteParamValueReplay(std::ostream &os, const CallCapture &call, T value) |
| { |
| os << value; |
| } |
| } // namespace angle |
| |
| template <typename T> |
| void CaptureTextureAndSamplerParameter_params(GLenum pname, |
| const T *param, |
| angle::ParamCapture *paramCapture) |
| { |
| if (pname == GL_TEXTURE_BORDER_COLOR || pname == GL_TEXTURE_CROP_RECT_OES) |
| { |
| CaptureMemory(param, sizeof(T) * 4, paramCapture); |
| } |
| else |
| { |
| CaptureMemory(param, sizeof(T), paramCapture); |
| } |
| } |
| |
| #endif // LIBANGLE_FRAME_CAPTURE_H_ |