Enable frame capture on Android

This CL gets capture/replay working on Android again.
* Updates where Android frame captures are written
* Uses debug system properties to prime Android environment variables
* Adds a configurable target Context to the capture_replay sample
* Updates capture/replay documentation for Android

Bug: angleproject:4036
Test: Captured TRex on Android, replayed on Linux
Change-Id: I94b4f6dc77468cd179b9d884b4dcd4afa56bd28c
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/1928056
Reviewed-by: Courtney Goeltzenleuchter <courtneygo@google.com>
Reviewed-by: Jamie Madill <jmadill@chromium.org>
Commit-Queue: Cody Northrop <cnorthrop@google.com>
This commit is contained in:
Cody Northrop 2019-11-20 19:56:10 -07:00 коммит произвёл Commit Bot
Родитель d32650bb9e
Коммит 6430e5e003
4 изменённых файлов: 187 добавлений и 14 удалений

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

@ -68,3 +68,66 @@ $ ANGLE_CAPTURE_ENABLED=0 out/Debug/capture_replay_sample
```
Note that we specify `ANGLE_CAPTURE_ENABLED=0` to prevent re-capturing when running the replay.
## Capturing an Android application
In order to capture on Android, the following additional steps must be taken. These steps
presume you've built and installed the ANGLE APK with capture enabled, and selected ANGLE
as the GLES driver for your application.
1. Create the output directory
Determine your package name:
```
export PACKAGE_NAME com.android.gl2jni
```
Then create an output directory that it can write to:
```
$ adb shell mkdir -p /sdcard/Android/data/$PACKAGE_NAME/angle_capture
```
2. Set properties to use for environment variable
On Android, it is difficult to set an environment variable before starting native code.
To work around this, ANGLE will read debug system properties before starting the capture
and use them to prime environment variables used by the capture code.
Note: Mid-execution capture doesn't work for Android just yet, so frame_start must be
zero, which is the default. This it is sufficient to only set the end frame.
```
$ adb shell setprop debug.angle.capture.frame_end 200
```
There are other properties that can be set that match 1:1 with the env vars, but
they are not required for capture:
```
# Optional
$ adb shell setprop debug.angle.capture.enabled 0
$ adb shell setprop debug.angle.capture.out_dir foo
$ adb shell setprop debug.angle.capture.frame_start 0
```
3. Run the application, then pull the files to the capture_replay directory
```
$ cd samples/capture_replay
$ adb pull /sdcard/Android/data/$PACKAGE_NAME/angle_capture replay_files
$ cp replay_files/* .
```
4. Update your GN args to specifiy which context will be replayed.
By default Context ID 1 will be replayed. On Android, Context ID 2 is more typical, some apps
we've run go as high as ID 6.
Note: this solution is temporary until EGL capture is in place.
```
angle_capture_replay_sample_context_id = 2
```
5. Replay the capture on desktop
Until we have samples building for Android, the replay sample must be run on desktop.
We will also be plumbing replay files into perf and correctness tests which will run on Android.
```
$ autoninja -C out/Release capture_replay_sample
$ out/Release/capture_replay_sample
```

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

@ -7,6 +7,9 @@ import("../gni/angle.gni")
declare_args() {
# Determines if we build the capture_replay sample. Off by default.
angle_build_capture_replay_sample = false
# Decide which context to replay, starting with desktop default
angle_capture_replay_sample_context_id = 1
}
angle_executable("shader_translator") {
@ -228,17 +231,26 @@ if (angle_build_capture_replay_sample) {
# To use the capture replay sample first move your capture sources into
# the capture_replay folder and enable the gn arg above.
angle_sample("capture_replay_sample") {
_contextid = angle_capture_replay_sample_context_id
sources =
rebase_path(read_file("capture_replay/angle_capture_context1_files.txt",
"list lines"), ".", "capture_replay") +
rebase_path(
read_file(
"capture_replay/angle_capture_context${_contextid}_files.txt",
"list lines"),
".",
"capture_replay") +
[
"capture_replay/CaptureReplay.cpp",
"capture_replay/angle_capture_context1.cpp",
"capture_replay/angle_capture_context1.h",
"capture_replay/angle_capture_context${_contextid}.cpp",
"capture_replay/angle_capture_context${_contextid}.h",
]
_data_path = rebase_path("capture_replay", root_out_dir)
defines = [ "ANGLE_CAPTURE_REPLAY_SAMPLE_DATA_DIR=\"${_data_path}\"" ]
defines = [
"ANGLE_CAPTURE_REPLAY_SAMPLE_DATA_DIR=\"${_data_path}\"",
"ANGLE_CAPTURE_REPLAY_SAMPLE_CONTEXT_ID=${_contextid}",
"ANGLE_CAPTURE_REPLAY_SAMPLE_HEADER=angle_capture_context${_contextid}.h",
]
suppressed_configs = [ "$angle_root:constructor_and_destructor_warnings" ]
}
}

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

@ -7,7 +7,24 @@
#include "SampleApplication.h"
#include "angle_capture_context1.h"
#include <functional>
#define ANGLE_MACRO_STRINGIZE_AUX(a) #a
#define ANGLE_MACRO_STRINGIZE(a) ANGLE_MACRO_STRINGIZE_AUX(a)
#define ANGLE_MACRO_CONCAT_AUX(a, b) a##b
#define ANGLE_MACRO_CONCAT(a, b) ANGLE_MACRO_CONCAT_AUX(a, b)
// Build the right context header based on replay ID
// This will expand to "angle_capture_context<#>.h"
#include ANGLE_MACRO_STRINGIZE(ANGLE_CAPTURE_REPLAY_SAMPLE_HEADER)
// Assign the context numbered functions based on GN arg selecting replay ID
std::function<void()> SetupContextReplay = reinterpret_cast<void (*)()>(
ANGLE_MACRO_CONCAT(SetupContext,
ANGLE_MACRO_CONCAT(ANGLE_CAPTURE_REPLAY_SAMPLE_CONTEXT_ID, Replay)));
std::function<void(int)> ReplayContextFrame = reinterpret_cast<void (*)(int)>(
ANGLE_MACRO_CONCAT(ReplayContext,
ANGLE_MACRO_CONCAT(ANGLE_CAPTURE_REPLAY_SAMPLE_CONTEXT_ID, Frame)));
class CaptureReplaySample : public SampleApplication
{
@ -23,7 +40,7 @@ class CaptureReplaySample : public SampleApplication
if (!angle::SetCWD(exeDir.c_str()))
return false;
SetBinaryDataDir(ANGLE_CAPTURE_REPLAY_SAMPLE_DATA_DIR);
SetupContext1Replay();
SetupContextReplay();
eglSwapInterval(getDisplay(), 1);
return true;
@ -36,7 +53,7 @@ class CaptureReplaySample : public SampleApplication
// Compute the current frame, looping from kReplayFrameStart to kReplayFrameEnd.
uint32_t frame =
kReplayFrameStart + (mCurrentFrame % (kReplayFrameEnd - kReplayFrameStart));
ReplayContext1Frame(frame);
ReplayContextFrame(frame);
mCurrentFrame++;
}

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

@ -14,6 +14,8 @@
#include <fstream>
#include <string>
#include "sys/stat.h"
#include "common/system_utils.h"
#include "libANGLE/Context.h"
#include "libANGLE/Framebuffer.h"
@ -33,6 +35,76 @@ namespace angle
{
namespace
{
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";
#if defined(ANGLE_PLATFORM_ANDROID)
constexpr char kAndroidCaptureEnabled[] = "debug.angle.capture.enabled";
constexpr char kAndroidOutDir[] = "debug.angle.capture.out_dir";
constexpr char kAndroidFrameStart[] = "debug.angle.capture.frame_start";
constexpr char kAndroidFrameEnd[] = "debug.angle.capture.frame_end";
constexpr int kStreamSize = 64;
constexpr char kAndroidOutputSubdir[] = "/angle_capture/";
// Call out to 'getprop' on a shell and return a string if the value was set
std::string AndroidGetEnvFromProp(const char *key)
{
std::string command("getprop ");
command += key;
// Run the command and open a I/O stream to read results
char stream[kStreamSize] = {};
FILE *pipe = popen(command.c_str(), "r");
if (pipe != nullptr)
{
fgets(stream, kStreamSize, pipe);
pclose(pipe);
}
// Right strip white space
std::string result(stream);
result.erase(result.find_last_not_of(" \n\r\t") + 1);
return result;
}
void PrimeAndroidEnvironmentVariables()
{
std::string enabled = AndroidGetEnvFromProp(kAndroidCaptureEnabled);
if (!enabled.empty())
{
INFO() << "Frame capture read " << enabled << " from " << kAndroidCaptureEnabled;
setenv(kEnabledVarName, enabled.c_str(), 1);
}
std::string outDir = AndroidGetEnvFromProp(kAndroidOutDir);
if (!outDir.empty())
{
INFO() << "Frame capture read " << outDir << " from " << kAndroidOutDir;
setenv(kOutDirectoryVarName, outDir.c_str(), 1);
}
std::string frameStart = AndroidGetEnvFromProp(kAndroidFrameStart);
if (!frameStart.empty())
{
INFO() << "Frame capture read " << frameStart << " from " << kAndroidFrameStart;
setenv(kFrameStartVarName, frameStart.c_str(), 1);
}
std::string frameEnd = AndroidGetEnvFromProp(kAndroidFrameEnd);
if (!frameEnd.empty())
{
INFO() << "Frame capture read " << frameEnd << " from " << kAndroidFrameEnd;
setenv(kFrameEndVarName, frameEnd.c_str(), 1);
}
}
#endif
std::string GetDefaultOutDirectory()
{
#if defined(ANGLE_PLATFORM_ANDROID)
@ -57,18 +129,23 @@ std::string GetDefaultOutDirectory()
{
ERR() << "not able to lookup application id";
}
path += std::string(applicationId) + "/";
path += std::string(applicationId) + kAndroidOutputSubdir;
// Check for existance of output path
struct stat dir_stat;
if (stat(path.c_str(), &dir_stat) == -1)
{
ERR() << "Output directory '" << path
<< "' does not exist. Create it over adb using mkdir.";
}
return path;
#else
return std::string("./");
#endif // defined(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";
struct FmtCapturePrefix
{
FmtCapturePrefix(int contextIdIn) : contextId(contextIdIn) {}
@ -1751,6 +1828,10 @@ FrameCapture::FrameCapture()
{
reset();
#if defined(ANGLE_PLATFORM_ANDROID)
PrimeAndroidEnvironmentVariables();
#endif
std::string enabledFromEnv = angle::GetEnvironmentVar(kEnabledVarName);
if (enabledFromEnv == "0")
{