// // 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>; 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 void addValueParam(const char *paramName, ParamType paramType, T paramValue); template void setValueParamAtIndex(const char *paramName, ParamType paramType, T paramValue, int index); template void addEnumParam(const char *paramName, gl::GLESEnum enumGroup, ParamType paramType, T paramValue); template 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 &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 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 &clientArraysSizebytes); ~ReplayContext(); template T getReadBufferPointer(const ParamCapture ¶m) { ASSERT(param.readBufferSizeBytes > 0); ASSERT(mReadBuffer.size() >= param.readBufferSizeBytes); return reinterpret_cast(mReadBuffer.data()); } template T getAsConstPointer(const ParamCapture ¶m) { if (param.arrayClientPointerIndex != -1) { return reinterpret_cast(mClientArraysBuffer[param.arrayClientPointerIndex].data()); } if (!param.data.empty()) { ASSERT(param.data.size() == 1); return reinterpret_cast(param.data[0].data()); } return nullptr; } template T getAsPointerConstPointer(const ParamCapture ¶m) { static_assert(sizeof(typename std::remove_pointer::type) == sizeof(uint8_t *), "pointer size not match!"); ASSERT(!param.data.empty()); mPointersBuffer.clear(); mPointersBuffer.reserve(param.data.size()); for (const std::vector &data : param.data) { mPointersBuffer.emplace_back(data.data()); } return reinterpret_cast(mPointersBuffer.data()); } gl::AttribArray> &getClientArraysBuffer() { return mClientArraysBuffer; } private: std::vector mReadBuffer; std::vector mPointersBuffer; gl::AttribArray> 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: // using Counter = std::pair; std::map mData; }; constexpr int kStringsNotFound = -1; class StringCounters final : angle::NonCopyable { public: StringCounters(); ~StringCounters(); int getStringCounter(const std::vector &str); void setStringCounter(const std::vector &str, int &counter); private: std::map, 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 &strings, bool *isNewEntryOut); void saveFrame(); void saveFrameIfFull(); void saveIndexFilesAndHeader(); void saveSetupFile(); std::vector 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 mReplayHeaders; std::vector mGlobalVariableDeclarations; std::vector mPublicFunctionPrototypes; std::vector mPublicFunctions; std::vector mPrivateFunctionPrototypes; std::vector mPrivateFunctions; std::vector mWrittenFiles; }; using BufferCalls = std::map>; // true means mapped, false means unmapped using BufferMapStatusMap = std::map; using FenceSyncSet = std::set; using FenceSyncCalls = std::map>; // 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; using DefaultUniformLocationsPerProgramMap = std::map; // A map of programs which maps to locations and their reset calls using DefaultUniformCallsPerLocationMap = std::map>; using DefaultUniformCallsPerProgramMap = std::map; using DefaultUniformBaseLocationMap = std::map, gl::UniformLocation>; using ResourceSet = std::set; using ResourceCalls = std::map>; 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(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 &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 &idsOut); std::map &getImageToAttribTable() { return mMatchImageToAttribs; } std::map &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 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 mTrackedResourcesPerContext; std::map mMatchImageToAttribs; std::map mMatchTextureIDToImage; }; // Used by the CPP replay to filter out unnecessary code. using HasResourceTypeMap = angle::PackedEnumBitSet; // Map of ResourceType to IDs and range of setup calls using ResourceIDToSetupCallsMap = PackedEnumMap>>; // Map of buffer ID to offset and size used when mapped using BufferDataMap = std::map>; // A dictionary of sources indexed by shader type. using ProgramSources = gl::ShaderMap; // Maps from IDs to sources. using ShaderSourceMap = std::map; using ProgramSourceMap = std::map; // Map from textureID to level and data using TextureLevels = std::map>; using TextureLevelDataMap = std::map; struct SurfaceParams { gl::Extents extents; egl::ColorSpace colorSpace; }; // Map from ContextID to SurfaceParams using SurfaceParamsMap = std::map; using CallVector = std::vector *>; // A map from API entry point to calls using CallResetMap = std::map>; // 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 &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 mDirtyEntryPoints; // Reset calls per API entry point CallResetMap mResetCalls; }; class FrameCapture final : angle::NonCopyable { public: FrameCapture(); ~FrameCapture(); std::vector &getSetupCalls() { return mSetupCalls; } void clearSetupCalls() { mSetupCalls.clear(); } StateResetHelper &getStateResetHelper() { return mStateResetHelper; } void reset(); private: std::vector 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 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 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, size_t> getBufferPagesForAddress(uintptr_t address); PageFaultHandlerRangeType handleWrite(uintptr_t address); bool haveBuffer(gl::BufferID id); public: std::mutex mMutex; HashMap> mBuffers; private: bool mEnabled = false; std::unique_ptr 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 &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 &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 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 *setupCalls, ResourceIDType type, GLuint id, gl::Range range); void updateReadBufferSize(size_t readBufferSize) { mReadBufferSize = std::max(mReadBufferSize, readBufferSize); } template void handleGennedResource(const gl::Context *context, ResourceType resourceID) { if (isCaptureActive()) { ResourceIDType idType = GetResourceIDTypeFromType::IDType; TrackedResource &tracker = mResourceTracker.getTrackedResource(context->id(), idType); tracker.setGennedResource(resourceID.value); } } template bool resourceIsGenerated(const gl::Context *context, ResourceType resourceID) { ResourceIDType idType = GetResourceIDTypeFromType::IDType; TrackedResource &tracker = mResourceTracker.getTrackedResource(context->id(), idType); return tracker.resourceIsGenerated(resourceID.value); } template void handleDeletedResource(const gl::Context *context, ResourceType resourceID) { if (isCaptureActive()) { ResourceIDType idType = GetResourceIDTypeFromType::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 &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 &newCalls); void maybeCapturePreCallUpdates(const gl::Context *context, CallCapture &call, std::vector *shareGroupSetupCalls, ResourceIDToSetupCallsMap *resourceIDToSetupCalls); template 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 &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 &setupCalls); static void ReplayCall(gl::Context *context, ReplayContext *replayContext, const CallCapture &call); std::vector 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 mBinaryData; bool mEnabled; bool mSerializeStateEnabled; std::string mOutDirectory; std::string mCaptureLabel; bool mCompression; gl::AttribArray mClientVertexArrayMap; uint32_t mFrameIndex; uint32_t mCaptureStartFrame; uint32_t mCaptureEndFrame; bool mIsFirstFrame = true; bool mWroteIndexFile = false; SurfaceParamsMap mDrawSurfaceParams; gl::AttribArray mClientArraySizes; size_t mReadBufferSize; HasResourceTypeMap mHasResourceType; ResourceIDToSetupCallsMap mResourceIDToSetupCalls; BufferDataMap mBufferDataMap; bool mValidateSerializedState = false; std::string mValidationExpression; bool mTrimEnabled = true; PackedEnumMap 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 mActiveFrameIndices; // Cache most recently compiled and linked sources. ShaderSourceMap mCachedShaderSource; ProgramSourceMap mCachedProgramSources; gl::ContextID mWindowSurfaceContextID; std::vector mShareGroupSetupCalls; }; template 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 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 void ParamBuffer::setValueParamAtIndex(const char *paramName, ParamType paramType, T paramValue, int index) { ASSERT(mParamCaptures.size() > static_cast(index)); ParamCapture capture(paramName, paramType); InitParamValue(paramType, paramValue, &capture.value); mParamCaptures[index] = std::move(capture); } template 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 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 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 void CaptureGenHandles(GLsizei n, T *handles, ParamCapture *paramCapture) { paramCapture->dataNElements = n; CaptureGenHandlesImpl(n, reinterpret_cast(handles), paramCapture); } template 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 void WriteParamValueReplay(std::ostream &os, const CallCapture &call, T value); template <> void WriteParamValueReplay(std::ostream &os, const CallCapture &call, GLboolean value); template <> void WriteParamValueReplay(std::ostream &os, const CallCapture &call, GLboolean *value); template <> void WriteParamValueReplay(std::ostream &os, const CallCapture &call, const void *value); template <> void WriteParamValueReplay(std::ostream &os, const CallCapture &call, void *value); template <> void WriteParamValueReplay(std::ostream &os, const CallCapture &call, const GLfloat *value); template <> void WriteParamValueReplay(std::ostream &os, const CallCapture &call, const GLint *value); template <> void WriteParamValueReplay(std::ostream &os, const CallCapture &call, GLsizei *value); template <> void WriteParamValueReplay(std::ostream &os, const CallCapture &call, const GLuint *value); template <> void WriteParamValueReplay(std::ostream &os, const CallCapture &call, GLDEBUGPROCKHR value); template <> void WriteParamValueReplay(std::ostream &os, const CallCapture &call, GLDEBUGPROC value); template <> void WriteParamValueReplay(std::ostream &os, const CallCapture &call, gl::BufferID value); template <> void WriteParamValueReplay(std::ostream &os, const CallCapture &call, gl::FenceNVID value); template <> void WriteParamValueReplay(std::ostream &os, const CallCapture &call, gl::FramebufferID value); template <> void WriteParamValueReplay(std::ostream &os, const CallCapture &call, gl::MemoryObjectID value); template <> void WriteParamValueReplay(std::ostream &os, const CallCapture &call, gl::ProgramPipelineID value); template <> void WriteParamValueReplay(std::ostream &os, const CallCapture &call, gl::QueryID value); template <> void WriteParamValueReplay(std::ostream &os, const CallCapture &call, gl::RenderbufferID value); template <> void WriteParamValueReplay(std::ostream &os, const CallCapture &call, gl::SamplerID value); template <> void WriteParamValueReplay(std::ostream &os, const CallCapture &call, gl::SemaphoreID value); template <> void WriteParamValueReplay(std::ostream &os, const CallCapture &call, gl::ShaderProgramID value); template <> void WriteParamValueReplay(std::ostream &os, const CallCapture &call, gl::TextureID value); template <> void WriteParamValueReplay(std::ostream &os, const CallCapture &call, gl::TransformFeedbackID value); template <> void WriteParamValueReplay(std::ostream &os, const CallCapture &call, gl::VertexArrayID value); template <> void WriteParamValueReplay(std::ostream &os, const CallCapture &call, gl::UniformLocation value); template <> void WriteParamValueReplay(std::ostream &os, const CallCapture &call, gl::UniformBlockIndex value); template <> void WriteParamValueReplay(std::ostream &os, const CallCapture &call, GLsync value); template <> void WriteParamValueReplay(std::ostream &os, const CallCapture &call, GLeglImageOES value); template <> void WriteParamValueReplay(std::ostream &os, const CallCapture &call, GLubyte value); template <> void WriteParamValueReplay(std::ostream &os, const CallCapture &call, EGLContext value); template <> void WriteParamValueReplay(std::ostream &os, const CallCapture &call, EGLContext value); template <> void WriteParamValueReplay(std::ostream &os, const CallCapture &call, EGLContext value); template <> void WriteParamValueReplay(std::ostream &os, const CallCapture &call, EGLDEBUGPROCKHR value); template <> void WriteParamValueReplay(std::ostream &os, const CallCapture &call, EGLGetBlobFuncANDROID value); template <> void WriteParamValueReplay(std::ostream &os, const CallCapture &call, EGLSetBlobFuncANDROID value); template <> void WriteParamValueReplay(std::ostream &os, const CallCapture &call, EGLClientBuffer value); template <> void WriteParamValueReplay(std::ostream &os, const CallCapture &call, EGLConfig value); template <> void WriteParamValueReplay(std::ostream &os, const CallCapture &call, EGLSurface value); // General fallback for any unspecific type. template void WriteParamValueReplay(std::ostream &os, const CallCapture &call, T value) { os << value; } } // namespace angle template 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_