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 @@ ...@@ -13,6 +13,7 @@
#include <cstring> #include <cstring>
#include <string> #include <string>
#include "common/system_utils.h"
#include "libANGLE/Context.h" #include "libANGLE/Context.h"
#include "libANGLE/VertexArray.h" #include "libANGLE/VertexArray.h"
#include "libANGLE/gl_enum_utils_autogen.h" #include "libANGLE/gl_enum_utils_autogen.h"
...@@ -21,44 +22,45 @@ ...@@ -21,44 +22,45 @@
# 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
#ifdef ANGLE_PLATFORM_ANDROID namespace angle
# define ANGLE_CAPTURE_PATH ("/sdcard/Android/data/" + CurrentAPKName() + "/") {
namespace
std::string CurrentAPKName() {
std::string GetDefaultOutDirectory()
{ {
static char sApplicationId[512] = {0}; #if defined(ANGLE_PLATFORM_ANDROID)
if (!sApplicationId[0]) 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 fread(sApplicationId, 1, sizeof(sApplicationId), cmdline);
FILE *cmdline = fopen("/proc/self/cmdline", "r"); fclose(cmdline);
if (cmdline)
{
fread(sApplicationId, 1, sizeof(sApplicationId), cmdline);
fclose(cmdline);
// Some package may have application id as <app_name>:<cmd_name> // Some package may have application id as <app_name>:<cmd_name>
char *colonSep = strchr(sApplicationId, ':'); char *colonSep = strchr(sApplicationId, ':');
if (colonSep) if (colonSep)
{
*colonSep = '\0';
}
}
else
{ {
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 constexpr char kEnabledVarName[] = "ANGLE_CAPTURE_ENABLED";
# define ANGLE_CAPTURE_PATH "./" constexpr char kOutDirectoryVarName[] = "ANGLE_CAPTURE_OUT_DIR";
#endif // ANGLE_PLATFORM_ANDROID 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::string GetCaptureFileName(int contextId, uint32_t frameIndex, const char *suffix)
{ {
std::stringstream fnameStream; std::stringstream fnameStream;
...@@ -67,9 +69,12 @@ std::string GetCaptureFileName(int contextId, uint32_t frameIndex, const char *s ...@@ -67,9 +69,12 @@ std::string GetCaptureFileName(int contextId, uint32_t frameIndex, const char *s
return fnameStream.str(); 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, void WriteParamStaticVarName(const CallCapture &call,
...@@ -366,7 +371,8 @@ bool AnyClientArray(const gl::AttribArray<size_t> &clientArraySizes) ...@@ -366,7 +371,8 @@ bool AnyClientArray(const gl::AttribArray<size_t> &clientArraySizes)
return false; return false;
} }
void WriteCppReplay(int contextId, void WriteCppReplay(const std::string &outDir,
int contextId,
uint32_t frameIndex, uint32_t frameIndex,
const std::vector<CallCapture> &calls, const std::vector<CallCapture> &calls,
const gl::AttribArray<size_t> &clientArraySizes, const gl::AttribArray<size_t> &clientArraySizes,
...@@ -468,7 +474,7 @@ void WriteCppReplay(int contextId, ...@@ -468,7 +474,7 @@ void WriteCppReplay(int contextId,
if (!binaryData.empty()) 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"); FILE *fp = fopen(dataFilepath.c_str(), "wb");
if (!fp) if (!fp)
...@@ -502,7 +508,7 @@ void WriteCppReplay(int contextId, ...@@ -502,7 +508,7 @@ void WriteCppReplay(int contextId,
std::string outString = out.str(); std::string outString = out.str();
std::string headerString = header.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"); FILE *fp = fopen(cppFilePath.c_str(), "w");
if (!fp) if (!fp)
{ {
...@@ -645,9 +651,38 @@ ReplayContext::ReplayContext(size_t readBufferSizebytes, ...@@ -645,9 +651,38 @@ ReplayContext::ReplayContext(size_t readBufferSizebytes,
} }
ReplayContext::~ReplayContext() {} ReplayContext::~ReplayContext() {}
FrameCapture::FrameCapture() : mFrameIndex(0), mReadBufferSize(0) FrameCapture::FrameCapture()
: mEnabled(true), mFrameIndex(0), mFrameStart(0), mFrameEnd(10), mReadBufferSize(0)
{ {
reset(); 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; FrameCapture::~FrameCapture() = default;
...@@ -926,7 +961,8 @@ void FrameCapture::onEndFrame(const gl::Context *context) ...@@ -926,7 +961,8 @@ void FrameCapture::onEndFrame(const gl::Context *context)
{ {
if (!mCalls.empty()) if (!mCalls.empty())
{ {
WriteCppReplay(context->id(), mFrameIndex, mCalls, mClientArraySizes, mReadBufferSize); WriteCppReplay(mOutDirectory, context->id(), mFrameIndex, mCalls, mClientArraySizes,
mReadBufferSize);
reset(); reset();
mFrameIndex++; mFrameIndex++;
} }
...@@ -944,7 +980,7 @@ int DataCounters::getAndIncrement(gl::EntryPoint entryPoint, const std::string & ...@@ -944,7 +980,7 @@ int DataCounters::getAndIncrement(gl::EntryPoint entryPoint, const std::string &
bool FrameCapture::enabled() const bool FrameCapture::enabled() const
{ {
return mFrameIndex < 100; return mEnabled && mFrameIndex >= mFrameStart && mFrameIndex <= mFrameEnd;
} }
void FrameCapture::replay(gl::Context *context) void FrameCapture::replay(gl::Context *context)
......
...@@ -187,9 +187,13 @@ class FrameCapture final : angle::NonCopyable ...@@ -187,9 +187,13 @@ class FrameCapture final : angle::NonCopyable
const CallCapture &call, const CallCapture &call,
const ParamCapture &param); const ParamCapture &param);
bool mEnabled;
std::string mOutDirectory;
std::vector<CallCapture> mCalls; std::vector<CallCapture> mCalls;
gl::AttribArray<int> mClientVertexArrayMap; gl::AttribArray<int> mClientVertexArrayMap;
uint32_t mFrameIndex; uint32_t mFrameIndex;
uint32_t mFrameStart;
uint32_t mFrameEnd;
gl::AttribArray<size_t> mClientArraySizes; gl::AttribArray<size_t> mClientArraySizes;
size_t mReadBufferSize; 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