diff --git a/doc/CaptureAndReplay.md b/doc/CaptureAndReplay.md index 44db52a89..f93af4c16 100644 --- a/doc/CaptureAndReplay.md +++ b/doc/CaptureAndReplay.md @@ -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 diff --git a/src/libANGLE/FrameCapture.cpp b/src/libANGLE/FrameCapture.cpp index 502d363bc..d51f800cb 100644 --- a/src/libANGLE/FrameCapture.cpp +++ b/src/libANGLE/FrameCapture.cpp @@ -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::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) { diff --git a/src/libANGLE/FrameCapture.h b/src/libANGLE/FrameCapture.h index f168cdeaa..ba441bd8e 100644 --- a/src/libANGLE/FrameCapture.h +++ b/src/libANGLE/FrameCapture.h @@ -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