Capture/Replay: Use zlib to compress data files.

Gives about a 2-3x reduced data file size. Should help reduce the time
we spent transferring trace files on Android.

This feature is implemented as a parameter to the capture/replay tool.
This keeps backwards compatiblity if we ever need to integrate a cpp
replay into an environment that doesn't have access to zlib for
decompression.

We'll need to update the traces to take advantage of the compression.

Bug: angleproject:4484
Change-Id: Id7994efe3c0d529b85fa7e7f1b00444e630dd2cd
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2104555
Commit-Queue: Jamie Madill <jmadill@chromium.org>
Reviewed-by: Jamie Madill <jmadill@chromium.org>
This commit is contained in:
Jamie Madill 2020-03-15 22:11:03 -04:00 коммит произвёл Commit Bot
Родитель 78a85f2c18
Коммит 7e453a2537
6 изменённых файлов: 135 добавлений и 18 удалений

Просмотреть файл

@ -744,8 +744,21 @@ angle_source_set("angle_gl_enum_utils") {
] ]
} }
config("angle_compression_config") {
include_dirs = [ "//third_party/zlib/google" ]
}
group("angle_compression") {
public_configs = [
":angle_compression_config",
"//third_party/zlib:zlib_config",
]
public_deps = [ "//third_party/zlib/google:compression_utils_portable" ]
}
angle_source_set("libANGLE_with_capture") { angle_source_set("libANGLE_with_capture") {
public_deps = [ ":libANGLE_base" ] public_deps = [ ":libANGLE_base" ]
deps = [ ":angle_compression" ]
public_configs = [ ":angle_frame_capture_enabled" ] public_configs = [ ":angle_frame_capture_enabled" ]
sources = libangle_capture_sources sources = libangle_capture_sources
} }

Просмотреть файл

@ -32,6 +32,8 @@ Some simple environment variables control frame capture:
* `ANGLE_CAPTURE_ENABLED`: * `ANGLE_CAPTURE_ENABLED`:
* Set to `0` to disable capture entirely. Default is `1`. * Set to `0` to disable capture entirely. Default is `1`.
* `ANGLE_CAPTURE_COMPRESSION`:
* Set to `0` to disable capture compression. Default is `1`.
* `ANGLE_CAPTURE_OUT_DIR=<path>`: * `ANGLE_CAPTURE_OUT_DIR=<path>`:
* Can specify an alternate replay output directory. * Can specify an alternate replay output directory.
* Example: `ANGLE_CAPTURE_OUT_DIR=samples/capture_replay`. Default is the CWD. * Example: `ANGLE_CAPTURE_OUT_DIR=samples/capture_replay`. Default is the CWD.

Просмотреть файл

@ -29,6 +29,9 @@
#include "libANGLE/queryconversions.h" #include "libANGLE/queryconversions.h"
#include "libANGLE/queryutils.h" #include "libANGLE/queryutils.h"
#define USE_SYSTEM_ZLIB
#include "compression_utils_portable.h"
#if !ANGLE_CAPTURE_ENABLED #if !ANGLE_CAPTURE_ENABLED
# error Frame capture must be enbled to include this file. # error Frame capture must be enbled to include this file.
#endif // !ANGLE_CAPTURE_ENABLED #endif // !ANGLE_CAPTURE_ENABLED
@ -43,6 +46,7 @@ constexpr char kOutDirectoryVarName[] = "ANGLE_CAPTURE_OUT_DIR";
constexpr char kFrameStartVarName[] = "ANGLE_CAPTURE_FRAME_START"; constexpr char kFrameStartVarName[] = "ANGLE_CAPTURE_FRAME_START";
constexpr char kFrameEndVarName[] = "ANGLE_CAPTURE_FRAME_END"; constexpr char kFrameEndVarName[] = "ANGLE_CAPTURE_FRAME_END";
constexpr char kCaptureLabel[] = "ANGLE_CAPTURE_LABEL"; constexpr char kCaptureLabel[] = "ANGLE_CAPTURE_LABEL";
constexpr char kCompression[] = "ANGLE_CAPTURE_COMPRESSION";
#if defined(ANGLE_PLATFORM_ANDROID) #if defined(ANGLE_PLATFORM_ANDROID)
@ -51,6 +55,7 @@ constexpr char kAndroidOutDir[] = "debug.angle.capture.out_dir";
constexpr char kAndroidFrameStart[] = "debug.angle.capture.frame_start"; constexpr char kAndroidFrameStart[] = "debug.angle.capture.frame_start";
constexpr char kAndroidFrameEnd[] = "debug.angle.capture.frame_end"; constexpr char kAndroidFrameEnd[] = "debug.angle.capture.frame_end";
constexpr char kAndroidCaptureLabel[] = "debug.angle.capture.label"; constexpr char kAndroidCaptureLabel[] = "debug.angle.capture.label";
constexpr char kAndroidCompression[] = "debug.angle.capture.compression";
constexpr int kStreamSize = 64; constexpr int kStreamSize = 64;
@ -110,9 +115,16 @@ void PrimeAndroidEnvironmentVariables()
std::string captureLabel = AndroidGetEnvFromProp(kAndroidCaptureLabel); std::string captureLabel = AndroidGetEnvFromProp(kAndroidCaptureLabel);
if (!captureLabel.empty()) if (!captureLabel.empty())
{ {
INFO() << "Capture label read " << captureLabel << " from " << kAndroidCaptureLabel; INFO() << "Frame capture read " << captureLabel << " from " << kAndroidCaptureLabel;
setenv(kCaptureLabel, captureLabel.c_str(), 1); setenv(kCaptureLabel, captureLabel.c_str(), 1);
} }
std::string compression = AndroidGetEnvFromProp(kAndroidCompression);
if (!compression.empty())
{
INFO() << "Frame capture read " << compression << " from " << kAndroidCompression;
setenv(kCompression, compression.c_str(), 1);
}
} }
#endif #endif
@ -604,32 +616,65 @@ struct SaveFileHelper
std::string filePath; std::string filePath;
}; };
std::string GetBinaryDataFilePath(int contextId, const std::string &captureLabel) std::string GetBinaryDataFilePath(bool compression, int contextId, const std::string &captureLabel)
{ {
std::stringstream fnameStream; std::stringstream fnameStream;
fnameStream << FmtCapturePrefix(contextId, captureLabel) << ".angledata"; fnameStream << FmtCapturePrefix(contextId, captureLabel) << ".angledata";
if (compression)
{
fnameStream << ".gz";
}
return fnameStream.str(); return fnameStream.str();
} }
void SaveBinaryData(const std::string &outDir, void SaveBinaryData(bool compression,
const std::string &outDir,
int contextId, int contextId,
const std::string &captureLabel, const std::string &captureLabel,
const std::vector<uint8_t> &binaryData) const std::vector<uint8_t> &binaryData)
{ {
std::string binaryDataFileName = GetBinaryDataFilePath(contextId, captureLabel); std::string binaryDataFileName = GetBinaryDataFilePath(compression, contextId, captureLabel);
std::string dataFilepath = outDir + binaryDataFileName; std::string dataFilepath = outDir + binaryDataFileName;
SaveFileHelper saveData(dataFilepath, std::ios::binary); SaveFileHelper saveData(dataFilepath, std::ios::binary);
saveData.ofs.write(reinterpret_cast<const char *>(binaryData.data()), binaryData.size());
if (compression)
{
// Save compressed data.
uLong uncompressedSize = static_cast<uLong>(binaryData.size());
uLong expectedCompressedSize = zlib_internal::GzipExpectedCompressedSize(uncompressedSize);
std::vector<uint8_t> compressedData(expectedCompressedSize, 0);
uLong compressedSize = expectedCompressedSize;
int zResult = zlib_internal::GzipCompressHelper(compressedData.data(), &compressedSize,
binaryData.data(), uncompressedSize,
nullptr, nullptr);
if (zResult != Z_OK)
{
FATAL() << "Error compressing binary data: " << zResult;
}
saveData.ofs.write(reinterpret_cast<const char *>(compressedData.data()), compressedSize);
}
else
{
saveData.ofs.write(reinterpret_cast<const char *>(binaryData.data()), binaryData.size());
}
} }
void WriteLoadBinaryDataCall(std::ostream &out, int contextId, const std::string &captureLabel) void WriteLoadBinaryDataCall(bool compression,
std::ostream &out,
int contextId,
const std::string &captureLabel)
{ {
std::string binaryDataFileName = GetBinaryDataFilePath(contextId, captureLabel); std::string binaryDataFileName = GetBinaryDataFilePath(compression, contextId, captureLabel);
out << " LoadBinaryData(\"" << binaryDataFileName << "\");\n"; out << " LoadBinaryData(\"" << binaryDataFileName << "\");\n";
} }
void WriteCppReplay(const std::string &outDir, void WriteCppReplay(bool compression,
const std::string &outDir,
int contextId, int contextId,
const std::string &captureLabel, const std::string &captureLabel,
uint32_t frameIndex, uint32_t frameIndex,
@ -661,7 +706,7 @@ void WriteCppReplay(const std::string &outDir,
std::stringstream setupCallStream; std::stringstream setupCallStream;
WriteLoadBinaryDataCall(setupCallStream, contextId, captureLabel); WriteLoadBinaryDataCall(compression, setupCallStream, contextId, captureLabel);
for (const CallCapture &call : setupCalls) for (const CallCapture &call : setupCalls)
{ {
@ -710,7 +755,8 @@ void WriteCppReplay(const std::string &outDir,
} }
} }
void WriteCppReplayIndexFiles(const std::string &outDir, void WriteCppReplayIndexFiles(bool compression,
const std::string &outDir,
int contextId, int contextId,
const std::string &captureLabel, const std::string &captureLabel,
uint32_t frameStart, uint32_t frameStart,
@ -772,6 +818,11 @@ void WriteCppReplayIndexFiles(const std::string &outDir,
header << "void " << FmtReplayFunction(contextId, frameIndex) << ";\n"; header << "void " << FmtReplayFunction(contextId, frameIndex) << ";\n";
} }
header << "\n"; header << "\n";
header << "constexpr bool kIsBinaryDataCompressed = " << (compression ? "true" : "false")
<< ";\n";
header << "\n";
header << "using DecompressCallback = uint8_t *(*)(const std::vector<uint8_t> &);\n";
header << "void SetBinaryDataDecompressCallback(DecompressCallback callback);\n";
header << "void SetBinaryDataDir(const char *dataDir);\n"; header << "void SetBinaryDataDir(const char *dataDir);\n";
header << "void LoadBinaryData(const char *fileName);\n"; header << "void LoadBinaryData(const char *fileName);\n";
header << "\n"; header << "\n";
@ -801,6 +852,7 @@ void WriteCppReplayIndexFiles(const std::string &outDir,
source << " (*resourceMap)[id] = returnedID;\n"; source << " (*resourceMap)[id] = returnedID;\n";
source << "}\n"; source << "}\n";
source << "\n"; source << "\n";
source << "DecompressCallback gDecompressCallback;\n";
source << "const char *gBinaryDataDir = \".\";\n"; source << "const char *gBinaryDataDir = \".\";\n";
source << "FramebufferChangeCallback gFramebufferChangeCallback;\n"; source << "FramebufferChangeCallback gFramebufferChangeCallback;\n";
source << "void *gFramebufferChangeCallbackUserData;\n"; source << "void *gFramebufferChangeCallbackUserData;\n";
@ -879,6 +931,11 @@ void WriteCppReplayIndexFiles(const std::string &outDir,
source << " }\n"; source << " }\n";
source << "}\n"; source << "}\n";
source << "\n"; source << "\n";
source << "void SetBinaryDataDecompressCallback(DecompressCallback callback)\n";
source << "{\n";
source << " gDecompressCallback = callback;\n";
source << "}\n";
source << "\n";
source << "void SetBinaryDataDir(const char *dataDir)\n"; source << "void SetBinaryDataDir(const char *dataDir)\n";
source << "{\n"; source << "{\n";
source << " gBinaryDataDir = dataDir;\n"; source << " gBinaryDataDir = dataDir;\n";
@ -901,8 +958,17 @@ void WriteCppReplayIndexFiles(const std::string &outDir,
source << " fseek(fp, 0, SEEK_END);\n"; source << " fseek(fp, 0, SEEK_END);\n";
source << " long size = ftell(fp);\n"; source << " long size = ftell(fp);\n";
source << " fseek(fp, 0, SEEK_SET);\n"; source << " fseek(fp, 0, SEEK_SET);\n";
source << " gBinaryData = new uint8_t[size];\n"; source << " if (gDecompressCallback)\n";
source << " (void)fread(gBinaryData, 1, size, fp);\n"; source << " {\n";
source << " std::vector<uint8_t> compressedData(size);\n";
source << " (void)fread(compressedData.data(), 1, size, fp);\n";
source << " gBinaryData = gDecompressCallback(compressedData);\n";
source << " }\n";
source << " else\n";
source << " {\n";
source << " gBinaryData = new uint8_t[size];\n";
source << " (void)fread(gBinaryData, 1, size, fp);\n";
source << " }\n";
source << " fclose(fp);\n"; source << " fclose(fp);\n";
source << "}\n"; source << "}\n";
@ -2487,6 +2553,7 @@ ReplayContext::~ReplayContext() {}
FrameCapture::FrameCapture() FrameCapture::FrameCapture()
: mEnabled(true), : mEnabled(true),
mCompression(true),
mClientVertexArrayMap{}, mClientVertexArrayMap{},
mFrameIndex(0), mFrameIndex(0),
mFrameStart(0), mFrameStart(0),
@ -2541,6 +2608,12 @@ FrameCapture::FrameCapture()
// Optional label to provide unique file names and namespaces // Optional label to provide unique file names and namespaces
mCaptureLabel = labelFromEnv; mCaptureLabel = labelFromEnv;
} }
std::string compressionFromEnv = angle::GetEnvironmentVar(kCompression);
if (compressionFromEnv == "0")
{
mCompression = false;
}
} }
FrameCapture::~FrameCapture() = default; FrameCapture::~FrameCapture() = default;
@ -2984,19 +3057,20 @@ void FrameCapture::onEndFrame(const gl::Context *context)
// Note that we currently capture before the start frame to collect shader and program sources. // Note that we currently capture before the start frame to collect shader and program sources.
if (!mFrameCalls.empty() && mFrameIndex >= mFrameStart) if (!mFrameCalls.empty() && mFrameIndex >= mFrameStart)
{ {
WriteCppReplay(mOutDirectory, context->id(), mCaptureLabel, mFrameIndex, mFrameCalls, WriteCppReplay(mCompression, mOutDirectory, context->id(), mCaptureLabel, mFrameIndex,
mSetupCalls, &mBinaryData); mFrameCalls, mSetupCalls, &mBinaryData);
// Save the index files after the last frame. // Save the index files after the last frame.
if (mFrameIndex == mFrameEnd) if (mFrameIndex == mFrameEnd)
{ {
WriteCppReplayIndexFiles(mOutDirectory, context->id(), mCaptureLabel, mFrameStart, WriteCppReplayIndexFiles(mCompression, mOutDirectory, context->id(), mCaptureLabel,
mFrameEnd, mReadBufferSize, mClientArraySizes, mFrameStart, mFrameEnd, mReadBufferSize, mClientArraySizes,
mHasResourceType); mHasResourceType);
if (!mBinaryData.empty()) if (!mBinaryData.empty())
{ {
SaveBinaryData(mOutDirectory, context->id(), mCaptureLabel, mBinaryData); SaveBinaryData(mCompression, mOutDirectory, context->id(), mCaptureLabel,
mBinaryData);
mBinaryData.clear(); mBinaryData.clear();
} }
} }

Просмотреть файл

@ -225,6 +225,7 @@ class FrameCapture final : angle::NonCopyable
bool mEnabled; bool mEnabled;
std::string mOutDirectory; std::string mOutDirectory;
std::string mCaptureLabel; std::string mCaptureLabel;
bool mCompression;
gl::AttribArray<int> mClientVertexArrayMap; gl::AttribArray<int> mClientVertexArrayMap;
uint32_t mFrameIndex; uint32_t mFrameIndex;
uint32_t mFrameStart; uint32_t mFrameStart;

Просмотреть файл

@ -289,7 +289,10 @@ if (is_win || is_linux || is_android || is_mac || is_fuchsia) {
sources = angle_trace_perf_sources sources = angle_trace_perf_sources
defines = angle_trace_perf_defines defines = angle_trace_perf_defines
data = angle_trace_perf_data data = angle_trace_perf_data
deps = [ ":angle_perftests_shared" ] deps = [
":angle_perftests_shared",
"$angle_root:angle_compression",
]
suppressed_configs += suppressed_configs +=
[ "$angle_root:constructor_and_destructor_warnings" ] [ "$angle_root:constructor_and_destructor_warnings" ]

Просмотреть файл

@ -27,6 +27,9 @@
#include <functional> #include <functional>
#include <sstream> #include <sstream>
#define USE_SYSTEM_ZLIB
#include "compression_utils_portable.h"
using namespace angle; using namespace angle;
using namespace egl_platform; using namespace egl_platform;
@ -34,6 +37,26 @@ namespace
{ {
void FramebufferChangeCallback(void *userData, GLenum target, GLuint framebuffer); void FramebufferChangeCallback(void *userData, GLenum target, GLuint framebuffer);
ANGLE_MAYBE_UNUSED uint8_t *DecompressBinaryData(const std::vector<uint8_t> &compressedData)
{
uint32_t uncompressedSize =
zlib_internal::GetGzipUncompressedSize(compressedData.data(), compressedData.size());
std::unique_ptr<uint8_t[]> uncompressedData(new uint8_t[uncompressedSize]);
uLong destLen = uncompressedSize;
int zResult =
zlib_internal::GzipUncompressHelper(uncompressedData.get(), &destLen, compressedData.data(),
static_cast<uLong>(compressedData.size()));
if (zResult != Z_OK)
{
std::cerr << "Failure to decompressed binary data: " << zResult << "\n";
return nullptr;
}
return uncompressedData.release();
}
enum class TracePerfTestID enum class TracePerfTestID
{ {
Manhattan10, Manhattan10,
@ -154,6 +177,7 @@ TracePerfTest::TracePerfTest()
: ANGLERenderTest("TracePerf", GetParam()), mStartFrame(0), mEndFrame(0) : ANGLERenderTest("TracePerf", GetParam()), mStartFrame(0), mEndFrame(0)
{} {}
// TODO(jmadill/cnorthrop): Use decompression path. http://anglebug.com/3630
#define TRACE_TEST_CASE(NAME) \ #define TRACE_TEST_CASE(NAME) \
mStartFrame = NAME::kReplayFrameStart; \ mStartFrame = NAME::kReplayFrameStart; \
mEndFrame = NAME::kReplayFrameEnd; \ mEndFrame = NAME::kReplayFrameEnd; \