Capture/Replay: Allow starting capture at an unknown frame

This CL adds a new way to set the start frame of a capture.
It adds a new environment variable called ANGLE_CAPTURE_TRIGGER
that, when set, will be used instead of frame start and end.

By setting ANGLE_CAPTURE_TRIGGER to a non-zero value, ANGLE will
capture that many frames when the value changes. For example,
on Android, we can set it with:

  adb shell setprop debug.angle.capture.trigger 20

When we reach the target content, set the value back to zero:

  adb shell setprop debug.angle.capture.trigger 0

and ANGLE will start capturing 20 frames.

Currently only hooked up for Android, but should be possible to
support on other platforms.

Test: Capture application frames using trigger
Bug: angleproject:4949
Change-Id: I469ef5c48feb78c85b8cda2fefd5df59e495bbe2
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2347858
Commit-Queue: Cody Northrop <cnorthrop@google.com>
Reviewed-by: Jamie Madill <jmadill@chromium.org>
Reviewed-by: Manh Nguyen <nguyenmh@google.com>
This commit is contained in:
Cody Northrop 2020-08-10 09:53:54 -06:00 коммит произвёл Commit Bot
Родитель 48d951e22d
Коммит a7cbb3f06f
3 изменённых файлов: 93 добавлений и 1 удалений

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

@ -167,6 +167,25 @@ as the GLES driver for your application.
$ out/Release/capture_replay_sample
```
### Starting capture at an arbitrary frame
In some scenarios, you don't know which frame you want to start on. You'll only know when target
content is being rendered. For that we've added a trigger that can allow starting the capture at
any time.
To use it, set the following environment variable, in addition to all the setup steps above. Set
the trigger value equal to the number of frames you'd like to capture.
```
adb shell setprop debug.angle.capture.trigger 20
```
When this value is set, `ANGLE_CAPTURE_FRAME_START` and `ANGLE_CAPTURE_FRAME_END` will be ignored.
While your content is rendering, wait until you arrive at the scene you'd like to capture. Then
set the value back to zero:
```
adb shell setprop debug.angle.capture.trigger 0
```
ANGLE will detect this change and start recording the requested number of frames.
## Testing
### Regression Testing Architecture

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

@ -51,6 +51,7 @@ 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";
constexpr char kCaptureTriggerVarName[] = "ANGLE_CAPTURE_TRIGGER";
constexpr char kCaptureLabel[] = "ANGLE_CAPTURE_LABEL";
constexpr char kCompression[] = "ANGLE_CAPTURE_COMPRESSION";
constexpr char kSerializeStateEnabledVarName[] = "ANGLE_CAPTURE_SERIALIZE_STATE";
@ -67,6 +68,7 @@ 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 char kAndroidCaptureTrigger[] = "debug.angle.capture.trigger";
constexpr char kAndroidCaptureLabel[] = "debug.angle.capture.label";
constexpr char kAndroidCompression[] = "debug.angle.capture.compression";
@ -125,6 +127,13 @@ void PrimeAndroidEnvironmentVariables()
setenv(kFrameEndVarName, frameEnd.c_str(), 1);
}
std::string captureTrigger = AndroidGetEnvFromProp(kAndroidCaptureTrigger);
if (!captureTrigger.empty())
{
INFO() << "Capture trigger read " << captureTrigger << " from " << kAndroidCaptureTrigger;
setenv(kCaptureTriggerVarName, captureTrigger.c_str(), 1);
}
std::string captureLabel = AndroidGetEnvFromProp(kAndroidCaptureLabel);
if (!captureLabel.empty())
{
@ -182,6 +191,15 @@ std::string GetDefaultOutDirectory()
#endif // defined(ANGLE_PLATFORM_ANDROID)
}
std::string GetCaptureTrigger()
{
#if defined(ANGLE_PLATFORM_ANDROID)
return AndroidGetEnvFromProp(kAndroidCaptureTrigger);
#else
return std::string();
#endif // defined(ANGLE_PLATFORM_ANDROID)
}
struct FmtCapturePrefix
{
FmtCapturePrefix(gl::ContextID contextIdIn, const std::string &captureLabelIn)
@ -3356,7 +3374,8 @@ FrameCapture::FrameCapture()
mFrameEnd(10),
mClientArraySizes{},
mReadBufferSize(0),
mHasResourceType{}
mHasResourceType{},
mCaptureTrigger(0)
{
reset();
@ -3398,6 +3417,19 @@ FrameCapture::FrameCapture()
mFrameEnd = atoi(endFromEnv.c_str());
}
std::string captureTriggerFromEnv = angle::GetEnvironmentVar(kCaptureTriggerVarName);
if (!captureTriggerFromEnv.empty())
{
mCaptureTrigger = atoi(captureTriggerFromEnv.c_str());
// If the trigger has been populated, ignore the other frame range variables by setting them
// to unreasonable values. This isn't perfect, but it is effective.
// TODO (anglebug.com/4949): Improve this, possibly by moving away from default start frame.
mFrameStart = mFrameEnd = std::numeric_limits<uint32_t>::max();
INFO() << "Capture trigger detected, overriding mFrameStart and mFrameEnd to "
<< mFrameStart;
}
std::string labelFromEnv = angle::GetEnvironmentVar(kCaptureLabel);
if (!labelFromEnv.empty())
{
@ -4065,8 +4097,43 @@ void FrameCapture::captureMappedBufferSnapshot(const gl::Context *context, const
(void)buffer->unmap(context, &dontCare);
}
void FrameCapture::checkForCaptureTrigger()
{
// If the capture trigger has not been set, move on
if (mCaptureTrigger == 0)
return;
// Otherwise, poll the value for a change
std::string captureTriggerStr = GetCaptureTrigger();
if (captureTriggerStr.empty())
return;
// If the value has changed, use the original value as the frame count
// TODO (anglebug.com/4949): Improve capture at unknown frame time. It is good to
// avoid polling if the feature is not enabled, but not entirely intuitive to set
// a value to zero when you want to trigger it.
uint32_t captureTrigger = atoi(captureTriggerStr.c_str());
if (captureTrigger != mCaptureTrigger)
{
// Start mid-execution capture for the next frame
mFrameStart = mFrameIndex + 1;
// Use the original trigger value as the frame count
mFrameEnd = mFrameStart + (mCaptureTrigger - 1);
INFO() << "Capture triggered at frame " << mFrameStart << " for " << mCaptureTrigger
<< " frames";
// Stop polling
mCaptureTrigger = 0;
}
}
void FrameCapture::onEndFrame(const gl::Context *context)
{
// On Android, we can trigger a capture during the run
checkForCaptureTrigger();
// Note that we currently capture before the start frame to collect shader and program sources.
if (!mFrameCalls.empty() && mFrameIndex >= mFrameStart)
{

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

@ -302,6 +302,7 @@ class FrameCapture final : angle::NonCopyable
~FrameCapture();
void captureCall(const gl::Context *context, CallCapture &&call);
void checkForCaptureTrigger();
void onEndFrame(const gl::Context *context);
void onDestroyContext(const gl::Context *context);
void onMakeCurrent(const egl::Surface *drawSurface);
@ -368,6 +369,11 @@ class FrameCapture final : angle::NonCopyable
// Cache a shadow copy of texture level data
TextureLevels mCachedTextureLevels;
TextureLevelDataMap mCachedTextureLevelData;
// If you don't know which frame you want to start capturing at, use the capture trigger.
// Initialize it to the number of frames you want to capture, and then clear the value to 0 when
// you reach the content you want to capture. Currently only available on Android.
uint32_t mCaptureTrigger;
};
template <typename CaptureFuncT, typename... ArgsT>