Commit a2e66a5e by Jamie Madill Committed by Commit Bot

Capture/Replay: Introduce environment options and docs.

Introduces a few ways of controlling capture via: * `ANGLE_CAPTURE_ENABLED`: Can be set to "0" to disable capture entirely. * `ANGLE_CAPTURE_OUT_DIR`: Can specify an alternate directory than the CWD. * `ANGLE_CAPTURE_FRAME_END`: This variable can override the default of 10 frame to capture. Also adds a simple usage guide document. Bug: angleproject:3611 Change-Id: I49d32d5bae2c490dcbeb0f1ce3c6038e433adfaa Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/1869542Reviewed-by: 's avatarTobin Ehlis <tobine@google.com> Commit-Queue: Jamie Madill <jmadill@chromium.org>
parent 6b652b33
# ANGLE OpenGL Frame Capture and Replay
ANGLE currently supports a limited OpenGL capture and replay framework.
Limitations:
* Many OpenGL ES functions are not yet implemented.
* EGL capture and replay is not yet supported.
* Mid-execution is not yet implemented.
* Capture only tested on desktop platforms currently.
* Replays currently are implemented as CPP files.
## Capturing and replaying an application
To build ANGLE with capture and replay enabled update your GN args:
```
angle_with_capture_by_default = true
```
Once built ANGLE will capture a frame's OpenGL calls to a CPP replay stored in the current working
directory. The capture files will be named `angle_capture_context{id}_frame{n}.cpp`. Each OpenGL
context has a unique Context ID to identify its proper replay files. ANGLE will write out large
binary blobs such as Texture or Buffer data to `angle_capture_context{id}_frame{n}.angledata`.
To run a CPP replay you must stitch together the source files into a GN target. The samples
framework is well-suited for implementing a CPP replay example. Alternately you could adapt an ANGLE
end-to-end test. The `angledata` files must be accessible to the replay.
## Controlling Frame Capture
Some simple environment variables control frame capture:
* `ANGLE_CAPTURE_ENABLED`:
Can be set to "0" to disable capture entirely.
* `ANGLE_CAPTURE_OUT_DIR=<path>`:
Can specify an alternate replay output directory than the CWD.
Example: `ANGLE_CAPTURE_OUT_DIR=samples/capture_replay`
* `ANGLE_CAPTURE_FRAME_END=<n>`:
By default ANGLE will capture the first ten frames. This variable can override the default.
Example: `ANGLE_CAPTURE_FRAME_END=4`
......@@ -13,6 +13,7 @@
#include <cstring>
#include <string>
#include "common/system_utils.h"
#include "libANGLE/Context.h"
#include "libANGLE/VertexArray.h"
#include "libANGLE/gl_enum_utils_autogen.h"
......@@ -21,44 +22,45 @@
# error Frame capture must be enbled to include this file.
#endif // !ANGLE_CAPTURE_ENABLED
#ifdef ANGLE_PLATFORM_ANDROID
# define ANGLE_CAPTURE_PATH ("/sdcard/Android/data/" + CurrentAPKName() + "/")
std::string CurrentAPKName()
namespace angle
{
namespace
{
std::string GetDefaultOutDirectory()
{
static char sApplicationId[512] = {0};
if (!sApplicationId[0])
#if defined(ANGLE_PLATFORM_ANDROID)
std::string path = "/sdcard/Android/data/";
// Linux interface to get application id of the running process
FILE *cmdline = fopen("/proc/self/cmdline", "r");
if (cmdline)
{
// Linux interface to get application id of the running process
FILE *cmdline = fopen("/proc/self/cmdline", "r");
if (cmdline)
{
fread(sApplicationId, 1, sizeof(sApplicationId), cmdline);
fclose(cmdline);
fread(sApplicationId, 1, sizeof(sApplicationId), cmdline);
fclose(cmdline);
// Some package may have application id as <app_name>:<cmd_name>
char *colonSep = strchr(sApplicationId, ':');
if (colonSep)
{
*colonSep = '\0';
}
}
else
// Some package may have application id as <app_name>:<cmd_name>
char *colonSep = strchr(sApplicationId, ':');
if (colonSep)
{
WARN() << "not able to lookup application id";
*colonSep = '\0';
}
}
return std::string(sApplicationId);
else
{
ERR() << "not able to lookup application id";
}
path += std::string(sApplicationId) + "/";
return path;
#else
return std::string("./");
#endif // defined(ANGLE_PLATFORM_ANDROID)
}
#else
# define ANGLE_CAPTURE_PATH "./"
#endif // ANGLE_PLATFORM_ANDROID
constexpr char kEnabledVarName[] = "ANGLE_CAPTURE_ENABLED";
constexpr char kOutDirectoryVarName[] = "ANGLE_CAPTURE_OUT_DIR";
constexpr char kFrameStartVarName[] = "ANGLE_CAPTURE_FRAME_START";
constexpr char kFrameEndVarName[] = "ANGLE_CAPTURE_FRAME_END";
namespace angle
{
namespace
{
std::string GetCaptureFileName(int contextId, uint32_t frameIndex, const char *suffix)
{
std::stringstream fnameStream;
......@@ -67,9 +69,12 @@ std::string GetCaptureFileName(int contextId, uint32_t frameIndex, const char *s
return fnameStream.str();
}
std::string GetCaptureFilePath(int contextId, uint32_t frameIndex, const char *suffix)
std::string GetCaptureFilePath(const std::string &outDir,
int contextId,
uint32_t frameIndex,
const char *suffix)
{
return ANGLE_CAPTURE_PATH + GetCaptureFileName(contextId, frameIndex, suffix);
return outDir + GetCaptureFileName(contextId, frameIndex, suffix);
}
void WriteParamStaticVarName(const CallCapture &call,
......@@ -366,7 +371,8 @@ bool AnyClientArray(const gl::AttribArray<size_t> &clientArraySizes)
return false;
}
void WriteCppReplay(int contextId,
void WriteCppReplay(const std::string &outDir,
int contextId,
uint32_t frameIndex,
const std::vector<CallCapture> &calls,
const gl::AttribArray<size_t> &clientArraySizes,
......@@ -468,7 +474,7 @@ void WriteCppReplay(int contextId,
if (!binaryData.empty())
{
std::string dataFilepath = GetCaptureFilePath(contextId, frameIndex, ".angledata");
std::string dataFilepath = GetCaptureFilePath(outDir, contextId, frameIndex, ".angledata");
FILE *fp = fopen(dataFilepath.c_str(), "wb");
if (!fp)
......@@ -502,7 +508,7 @@ void WriteCppReplay(int contextId,
std::string outString = out.str();
std::string headerString = header.str();
std::string cppFilePath = GetCaptureFilePath(contextId, frameIndex, ".cpp");
std::string cppFilePath = GetCaptureFilePath(outDir, contextId, frameIndex, ".cpp");
FILE *fp = fopen(cppFilePath.c_str(), "w");
if (!fp)
{
......@@ -645,9 +651,38 @@ ReplayContext::ReplayContext(size_t readBufferSizebytes,
}
ReplayContext::~ReplayContext() {}
FrameCapture::FrameCapture() : mFrameIndex(0), mReadBufferSize(0)
FrameCapture::FrameCapture()
: mEnabled(true), mFrameIndex(0), mFrameStart(0), mFrameEnd(10), mReadBufferSize(0)
{
reset();
std::string enabledFromEnv = angle::GetEnvironmentVar(kEnabledVarName);
if (enabledFromEnv == "0")
{
mEnabled = false;
}
std::string pathFromEnv = angle::GetEnvironmentVar(kOutDirectoryVarName);
if (pathFromEnv.empty())
{
mOutDirectory = GetDefaultOutDirectory();
}
else
{
mOutDirectory = pathFromEnv;
}
std::string startFromEnv = angle::GetEnvironmentVar(kFrameStartVarName);
if (!startFromEnv.empty())
{
WARN() << "Capture frame start is not yet supported. Defaulting to 0.";
}
std::string endFromEnv = angle::GetEnvironmentVar(kFrameEndVarName);
if (!endFromEnv.empty())
{
mFrameEnd = atoi(endFromEnv.c_str());
}
}
FrameCapture::~FrameCapture() = default;
......@@ -926,7 +961,8 @@ void FrameCapture::onEndFrame(const gl::Context *context)
{
if (!mCalls.empty())
{
WriteCppReplay(context->id(), mFrameIndex, mCalls, mClientArraySizes, mReadBufferSize);
WriteCppReplay(mOutDirectory, context->id(), mFrameIndex, mCalls, mClientArraySizes,
mReadBufferSize);
reset();
mFrameIndex++;
}
......@@ -944,7 +980,7 @@ int DataCounters::getAndIncrement(gl::EntryPoint entryPoint, const std::string &
bool FrameCapture::enabled() const
{
return mFrameIndex < 100;
return mEnabled && mFrameIndex >= mFrameStart && mFrameIndex <= mFrameEnd;
}
void FrameCapture::replay(gl::Context *context)
......
......@@ -187,9 +187,13 @@ class FrameCapture final : angle::NonCopyable
const CallCapture &call,
const ParamCapture &param);
bool mEnabled;
std::string mOutDirectory;
std::vector<CallCapture> mCalls;
gl::AttribArray<int> mClientVertexArrayMap;
uint32_t mFrameIndex;
uint32_t mFrameStart;
uint32_t mFrameEnd;
gl::AttribArray<size_t> mClientArraySizes;
size_t mReadBufferSize;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment