blob: c624a2b393d8304bac8f79979c1a862a30e17568 [file] [log] [blame] [edit]
//
// 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 <stddef.h>
#include <fstream>
#include <vector>
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<std::vector<uint8_t>> &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<uint8_t> &prepareLoadBlock(size_t blockId);
std::vector<uint8_t> &prepareStoreBlock(size_t blockId);
private:
bool mIsBinaryDataCompressed;
std::string mFileName;
size_t mIndexOffset = 0;
std::vector<FileBlockInfo> 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<ReplayBlockDescription> 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<std::vector<uint8_t>> 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_