| |
| // 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 inteface. |
| // |
| |
| #ifndef LIBANGLE_FRAME_CAPTURE_H_ |
| #define LIBANGLE_FRAME_CAPTURE_H_ |
| |
| #include "common/PackedEnums.h" |
| #include "libANGLE/Context.h" |
| #include "libANGLE/angletypes.h" |
| #include "libANGLE/entry_points_utils.h" |
| #include "libANGLE/frame_capture_utils_autogen.h" |
| |
| namespace gl |
| { |
| enum class GLenumGroup; |
| } |
| |
| namespace angle |
| { |
| 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::GLenumGroup enumGroup; // only used for param type GLenum, GLboolean and GLbitfield |
| std::vector<std::vector<uint8_t>> data; |
| 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 addEnumParam(const char *paramName, |
| gl::GLenumGroup 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(gl::EntryPoint entryPointIn, ParamBuffer &¶msIn); |
| CallCapture(const std::string &customFunctionNameIn, ParamBuffer &¶msIn); |
| ~CallCapture(); |
| |
| CallCapture(CallCapture &&other); |
| CallCapture &operator=(CallCapture &&other); |
| |
| const char *name() const; |
| |
| gl::EntryPoint entryPoint; |
| std::string customFunctionName; |
| ParamBuffer params; |
| }; |
| |
| 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(gl::EntryPoint entryPoint, const std::string ¶mName); |
| |
| private: |
| // <CallName, ParamName> |
| using Counter = std::pair<gl::EntryPoint, std::string>; |
| std::map<Counter, int> mData; |
| }; |
| |
| using BufferSet = std::set<gl::BufferID>; |
| using BufferCalls = std::map<gl::BufferID, std::vector<CallCapture>>; |
| |
| // Helper to track resource changes during the capture |
| class ResourceTracker final : angle::NonCopyable |
| { |
| public: |
| ResourceTracker(); |
| ~ResourceTracker(); |
| |
| BufferCalls &getBufferRegenCalls() { return mBufferRegenCalls; } |
| BufferCalls &getBufferRestoreCalls() { return mBufferRestoreCalls; } |
| |
| BufferSet &getStartingBuffers() { return mStartingBuffers; } |
| BufferSet &getNewBuffers() { return mNewBuffers; } |
| BufferSet &getBuffersToRegen() { return mBuffersToRegen; } |
| BufferSet &getBuffersToRestore() { return mBuffersToRestore; } |
| |
| void setGennedBuffer(gl::BufferID id); |
| void setDeletedBuffer(gl::BufferID id); |
| void setBufferModified(gl::BufferID id); |
| |
| private: |
| // Buffer regen calls will delete and gen a buffer |
| BufferCalls mBufferRegenCalls; |
| // Buffer restore calls will restore the contents of a buffer |
| BufferCalls mBufferRestoreCalls; |
| |
| // Starting buffers include all the buffers created during setup for MEC |
| BufferSet mStartingBuffers; |
| // New buffers are those generated while capturing |
| BufferSet mNewBuffers; |
| // Buffers to regen are a list of starting buffers that need to be deleted and genned |
| BufferSet mBuffersToRegen; |
| // Buffers to restore include any starting buffers with contents modified during the run |
| BufferSet mBuffersToRestore; |
| }; |
| |
| // Used by the CPP replay to filter out unnecessary code. |
| using HasResourceTypeMap = angle::PackedEnumBitSet<ResourceIDType>; |
| |
| // 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>; |
| |
| class FrameCapture final : angle::NonCopyable |
| { |
| public: |
| FrameCapture(); |
| ~FrameCapture(); |
| |
| void captureCall(const gl::Context *context, CallCapture &&call); |
| void onEndFrame(const gl::Context *context); |
| bool enabled() const { return mEnabled; } |
| |
| bool isCapturing() const; |
| void replay(gl::Context *context); |
| |
| private: |
| void captureClientArraySnapshot(const gl::Context *context, |
| size_t vertexCount, |
| size_t instanceCount); |
| void captureMappedBufferSnapshot(const gl::Context *context, const CallCapture &call); |
| |
| void captureCompressedTextureData(const gl::Context *context, const CallCapture &call); |
| |
| void reset(); |
| void maybeCaptureClientData(const gl::Context *context, CallCapture &call); |
| void maybeCapturePostCallUpdates(const gl::Context *context); |
| |
| static void ReplayCall(gl::Context *context, |
| ReplayContext *replayContext, |
| const CallCapture &call); |
| |
| std::vector<CallCapture> mSetupCalls; |
| std::vector<CallCapture> mFrameCalls; |
| |
| // 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 = false; |
| std::string mOutDirectory; |
| std::string mCaptureLabel; |
| bool mCompression; |
| gl::AttribArray<int> mClientVertexArrayMap; |
| uint32_t mFrameIndex; |
| uint32_t mFrameStart; |
| uint32_t mFrameEnd; |
| gl::AttribArray<size_t> mClientArraySizes; |
| size_t mReadBufferSize; |
| HasResourceTypeMap mHasResourceType; |
| BufferDataMap mBufferDataMap; |
| |
| ResourceTracker mResourceTracker; |
| |
| // Cache most recently compiled and linked sources. |
| ShaderSourceMap mCachedShaderSources; |
| ProgramSourceMap mCachedProgramSources; |
| |
| // Cache a shadow copy of texture level data |
| TextureLevels mCachedTextureLevels; |
| TextureLevelDataMap mCachedTextureLevelData; |
| }; |
| |
| template <typename CaptureFuncT, typename... ArgsT> |
| void CaptureCallToFrameCapture(CaptureFuncT captureFunc, |
| bool isCallValid, |
| gl::Context *context, |
| ArgsT... captureParams) |
| { |
| FrameCapture *frameCapture = context->getFrameCapture(); |
| if (!frameCapture->isCapturing()) |
| return; |
| |
| CallCapture call = captureFunc(context->getState(), isCallValid, captureParams...); |
| frameCapture->captureCall(context, std::move(call)); |
| } |
| |
| 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::addEnumParam(const char *paramName, |
| gl::GLenumGroup enumGroup, |
| ParamType paramType, |
| T paramValue) |
| { |
| ParamCapture capture(paramName, paramType); |
| InitParamValue(paramType, paramValue, &capture.value); |
| capture.enumGroup = enumGroup; |
| mParamCaptures.emplace_back(std::move(capture)); |
| } |
| |
| std::ostream &operator<<(std::ostream &os, const ParamCapture &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); |
| |
| gl::Program *GetLinkedProgramForCapture(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 CaptureGenHandlesImpl(GLsizei n, GLuint *handles, ParamCapture *paramCapture); |
| |
| template <typename T> |
| void CaptureGenHandles(GLsizei n, T *handles, ParamCapture *paramCapture) |
| { |
| CaptureGenHandlesImpl(n, reinterpret_cast<GLuint *>(handles), 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::TvoidConstPointer>(std::ostream &os, |
| const CallCapture &call, |
| const void *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::TGLsync>(std::ostream &os, |
| const CallCapture &call, |
| GLsync 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 |
| |
| #endif // LIBANGLE_FRAME_CAPTURE_H_ |