Commit 6430e5e0 by Cody Northrop Committed by Commit Bot

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/+/1928056Reviewed-by: 's avatarCourtney Goeltzenleuchter <courtneygo@google.com> Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org> Commit-Queue: Cody Northrop <cnorthrop@google.com>
parent d32650bb
...@@ -68,3 +68,66 @@ $ ANGLE_CAPTURE_ENABLED=0 out/Debug/capture_replay_sample ...@@ -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. 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
```
\ No newline at end of file
...@@ -7,6 +7,9 @@ import("../gni/angle.gni") ...@@ -7,6 +7,9 @@ import("../gni/angle.gni")
declare_args() { declare_args() {
# Determines if we build the capture_replay sample. Off by default. # Determines if we build the capture_replay sample. Off by default.
angle_build_capture_replay_sample = false 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") { angle_executable("shader_translator") {
...@@ -228,17 +231,26 @@ if (angle_build_capture_replay_sample) { ...@@ -228,17 +231,26 @@ if (angle_build_capture_replay_sample) {
# To use the capture replay sample first move your capture sources into # To use the capture replay sample first move your capture sources into
# the capture_replay folder and enable the gn arg above. # the capture_replay folder and enable the gn arg above.
angle_sample("capture_replay_sample") { angle_sample("capture_replay_sample") {
_contextid = angle_capture_replay_sample_context_id
sources = sources =
rebase_path(read_file("capture_replay/angle_capture_context1_files.txt", rebase_path(
"list lines"), ".", "capture_replay") + read_file(
"capture_replay/angle_capture_context${_contextid}_files.txt",
"list lines"),
".",
"capture_replay") +
[ [
"capture_replay/CaptureReplay.cpp", "capture_replay/CaptureReplay.cpp",
"capture_replay/angle_capture_context1.cpp", "capture_replay/angle_capture_context${_contextid}.cpp",
"capture_replay/angle_capture_context1.h", "capture_replay/angle_capture_context${_contextid}.h",
] ]
_data_path = rebase_path("capture_replay", root_out_dir) _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" ] suppressed_configs = [ "$angle_root:constructor_and_destructor_warnings" ]
} }
} }
......
...@@ -7,7 +7,24 @@ ...@@ -7,7 +7,24 @@
#include "SampleApplication.h" #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 class CaptureReplaySample : public SampleApplication
{ {
...@@ -23,7 +40,7 @@ class CaptureReplaySample : public SampleApplication ...@@ -23,7 +40,7 @@ class CaptureReplaySample : public SampleApplication
if (!angle::SetCWD(exeDir.c_str())) if (!angle::SetCWD(exeDir.c_str()))
return false; return false;
SetBinaryDataDir(ANGLE_CAPTURE_REPLAY_SAMPLE_DATA_DIR); SetBinaryDataDir(ANGLE_CAPTURE_REPLAY_SAMPLE_DATA_DIR);
SetupContext1Replay(); SetupContextReplay();
eglSwapInterval(getDisplay(), 1); eglSwapInterval(getDisplay(), 1);
return true; return true;
...@@ -36,7 +53,7 @@ class CaptureReplaySample : public SampleApplication ...@@ -36,7 +53,7 @@ class CaptureReplaySample : public SampleApplication
// Compute the current frame, looping from kReplayFrameStart to kReplayFrameEnd. // Compute the current frame, looping from kReplayFrameStart to kReplayFrameEnd.
uint32_t frame = uint32_t frame =
kReplayFrameStart + (mCurrentFrame % (kReplayFrameEnd - kReplayFrameStart)); kReplayFrameStart + (mCurrentFrame % (kReplayFrameEnd - kReplayFrameStart));
ReplayContext1Frame(frame); ReplayContextFrame(frame);
mCurrentFrame++; mCurrentFrame++;
} }
......
...@@ -14,6 +14,8 @@ ...@@ -14,6 +14,8 @@
#include <fstream> #include <fstream>
#include <string> #include <string>
#include "sys/stat.h"
#include "common/system_utils.h" #include "common/system_utils.h"
#include "libANGLE/Context.h" #include "libANGLE/Context.h"
#include "libANGLE/Framebuffer.h" #include "libANGLE/Framebuffer.h"
...@@ -33,6 +35,76 @@ namespace angle ...@@ -33,6 +35,76 @@ namespace angle
{ {
namespace 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() std::string GetDefaultOutDirectory()
{ {
#if defined(ANGLE_PLATFORM_ANDROID) #if defined(ANGLE_PLATFORM_ANDROID)
...@@ -57,18 +129,23 @@ std::string GetDefaultOutDirectory() ...@@ -57,18 +129,23 @@ std::string GetDefaultOutDirectory()
{ {
ERR() << "not able to lookup application id"; 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; return path;
#else #else
return std::string("./"); return std::string("./");
#endif // defined(ANGLE_PLATFORM_ANDROID) #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 struct FmtCapturePrefix
{ {
FmtCapturePrefix(int contextIdIn) : contextId(contextIdIn) {} FmtCapturePrefix(int contextIdIn) : contextId(contextIdIn) {}
...@@ -1751,6 +1828,10 @@ FrameCapture::FrameCapture() ...@@ -1751,6 +1828,10 @@ FrameCapture::FrameCapture()
{ {
reset(); reset();
#if defined(ANGLE_PLATFORM_ANDROID)
PrimeAndroidEnvironmentVariables();
#endif
std::string enabledFromEnv = angle::GetEnvironmentVar(kEnabledVarName); std::string enabledFromEnv = angle::GetEnvironmentVar(kEnabledVarName);
if (enabledFromEnv == "0") if (enabledFromEnv == "0")
{ {
......
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