// 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> data; 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 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 &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(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 &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(gl::EntryPoint entryPoint, const std::string ¶mName); private: // using Counter = std::pair; std::map mData; }; // Used by the CPP replay to filter out unnecessary code. using HasResourceTypeMap = angle::PackedEnumBitSet; // 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; 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 mSetupCalls; std::vector mFrameCalls; std::vector mTearDownCalls; // 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 = false; std::string mOutDirectory; std::string mCaptureLabel; bool mCompression; gl::AttribArray mClientVertexArrayMap; uint32_t mFrameIndex; uint32_t mFrameStart; uint32_t mFrameEnd; gl::AttribArray mClientArraySizes; size_t mReadBufferSize; HasResourceTypeMap mHasResourceType; BufferDataMap mBufferDataMap; // Cache most recently compiled and linked sources. ShaderSourceMap mCachedShaderSources; ProgramSourceMap mCachedProgramSources; // Cache a shadow copy of texture level data TextureLevels mCachedTextureLevels; TextureLevelDataMap mCachedTextureLevelData; }; template 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 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::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 void CaptureGenHandles(GLsizei n, T *handles, ParamCapture *paramCapture) { CaptureGenHandlesImpl(n, reinterpret_cast(handles), 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, const void *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, GLsync value); // General fallback for any unspecific type. template void WriteParamValueReplay(std::ostream &os, const CallCapture &call, T value) { os << value; } } // namespace angle #endif // LIBANGLE_FRAME_CAPTURE_H_