// // Copyright 2025 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. // // frame_capture_binary_data.h: // Common code for the ANGLE trace replay large trace binary data definition. // #ifndef FRAME_CAPTURE_BINARY_DATA_H_ #define FRAME_CAPTURE_BINARY_DATA_H_ #include "common/debug.h" #include #include #include namespace angle { constexpr size_t kDefaultBinaryDataSize = 0x80000000; constexpr size_t kDefaultDataBlockSize = 256 * 1024 * 1024; constexpr size_t kBinaryAlignment = 16; // The zlib doc recommends buffer sizes on the order of 128K or 256K bytes constexpr size_t kZlibBufferSize = 256 * 1024; constexpr uint32_t kInvalidBlockId = 0xFFFFFFFF; constexpr size_t kLongTraceVersionId = 1; // Index information ultimately saved in trace JSON file struct BinaryFileIndexInfo { size_t version; // Long file data description version number size_t blockSize; // Size of binary data blocks in bytes size_t blockCount; // Number of FileBlockInfo structures in file index trailer size_t residentSize; // Max amount of device memory used for binary data storage size_t indexOffset; // Offset in gzip file specifying start of file block descriptions BinaryFileIndexInfo() : version(1), blockSize(0), blockCount(0), residentSize(0), indexOffset(0) {} }; class FileStream; class FrameCaptureBinaryData { public: enum class Mode { Load, Store }; // Describes a block's location in the binary data file struct FileBlockInfo { size_t fileOffset; // Offset within the binary disk file size_t dataOffset; // Starting offset in the logical flat data view size_t dataSize; // Actual size of data in this block FileBlockInfo() : fileOffset(0), dataOffset(0), dataSize(0) {} }; // Describes a block's state during replay struct ReplayBlockDescription { size_t fileOffset; // Seek offset in binary data disk file size_t beginDataOffset; // Beginning flat binary data offset size_t endDataOffset; // Ending flat binary data offset (inclusive) size_t dataSize; // Size of data in this block uint8_t *residentAddress; // Memory address if resident, nullptr otherwise }; std::vector> &data() { return mData; } bool isSwapBlock(size_t blockId) { return blockId == mMaxResidentBlockIndex; } size_t totalSize() const; bool isSwapMode() const; bool isBlockResident(size_t blockId) const; void setBlockResident(size_t blockId, uint8_t *address); void setBlockNonResident(size_t blockId); void setBinaryDataSize(size_t binaryDataSize); void setBlockSize(size_t blockSize); void storeResidentBlocks(); // Format data for appending to compressed binary file BinaryFileIndexInfo appendFileIndex(); // Create binary memory block description index from binary file information void constructBlockDescIndex(size_t indexOffset); size_t append(const void *data, size_t size); void clear(); const uint8_t *getData(size_t offset); void initializeBinaryDataStore(bool compression, const std::string &outDir, const std::string &fileName); void storeBlock(); BinaryFileIndexInfo closeBinaryDataStore(); void configureBinaryDataLoader(bool compression, size_t blockCount, size_t blockSize, size_t residentSize, size_t indexOffset, const std::string &fileName); void initializeBinaryDataLoader(); void loadBlock(size_t blockId); void closeBinaryDataLoader(); void updateGetDataCache(size_t blockId); std::vector &prepareLoadBlock(size_t blockId); std::vector &prepareStoreBlock(size_t blockId); private: bool mIsBinaryDataCompressed; std::string mFileName; size_t mIndexOffset = 0; std::vector mFileIndex; uint32_t mStoredBlocks = 0; size_t mCurrentTransientLoadedBlockId = kInvalidBlockId; size_t mCurrentBlockOffset = 0; size_t mMaxResidentBinarySize = kDefaultBinaryDataSize; size_t mMaxResidentBlockIndex = (kDefaultBinaryDataSize / kDefaultDataBlockSize) - 1; // Binary data information size_t mDataBlockSize = kDefaultDataBlockSize; size_t mBlockCount = 0; // getData() fastpath cache information size_t mCacheBlockId = kInvalidBlockId; size_t mCacheBlockBeginOffset = 0; size_t mCacheBlockEndOffset = 0; uint8_t *mCacheBlockBaseAddress = nullptr; std::vector mReplayBlockDescriptions; // Chrome's allocator disallows creating one allocation that's bigger than 2GB, so the following // is one large buffer that is split in multiple pieces in memory. // Note: Make sure compression on write is disabled with a Chrome capture // (ANGLE_CAPTURE_COMPRESSION=0), since that would require bundling all data in one big // allocation. std::vector> mData; // Indicator that capture is complete and store can be finalized bool mCaptureComplete = false; FileStream *mFileStream = nullptr; }; constexpr int kSeekBegin = SEEK_SET; constexpr int kSeekEnd = SEEK_END; class FileStream { public: explicit FileStream(const std::string &filePath, FrameCaptureBinaryData::Mode mode) : mMode(mode), mFile(nullptr), mFilePath(filePath) { std::string openMode; if (mMode == FrameCaptureBinaryData::Mode::Store) { openMode = "wb+"; } else if (mMode == FrameCaptureBinaryData::Mode::Load) { openMode = "rb"; } else { FATAL() << "Invalid Mode enum in FileStream helper"; } mFile = fopen(mFilePath.c_str(), openMode.c_str()); if (!mFile) { FATAL() << "Could not open binary data file " << mFilePath; } } ~FileStream() { if (mFile) { fclose(mFile); mFile = nullptr; } } void write(const uint8_t *data, size_t size); size_t read(uint8_t *buffer, size_t size); void seek(long long offset, int whence); size_t getPosition(); private: FrameCaptureBinaryData::Mode mMode; FILE *mFile; std::string mFilePath; }; } // namespace angle #endif // FRAME_CAPTURE_BINARY_DATA_H_