Skip to content

perf(session-replay): Reduce capture stutters#7851

Draft
romtsn wants to merge 6 commits into
mainfrom
perf-session-replay-capture-backoff
Draft

perf(session-replay): Reduce capture stutters#7851
romtsn wants to merge 6 commits into
mainfrom
perf-session-replay-capture-backoff

Conversation

@romtsn
Copy link
Copy Markdown
Member

@romtsn romtsn commented Apr 29, 2026

Reduce session replay capture stutters by avoiding screenshots on the hottest UI frames and backing off when capture itself is slow.

The screenshot path is unchanged, so fidelity stays the same: this still uses the existing screenshot provider, masking, scale, and image pipeline. The change only adjusts when we ask for a screenshot. Captures are briefly deferred while the run loop is tracking, a scroll view/control/gesture is actively interacting, or the layer tree has enough active animations to indicate visible motion. Deferral is bounded to one second, so replay still produces frames during long interactions.

Slow synchronous captures now also feed an adaptive interval. If a capture takes at least 50ms, the next interval backs off up to five seconds, then recovers toward the configured frame rate after faster captures.

Benchmark comparison from the attached run on the iOS 26.4 simulator:

Variant FPS Slow frames p99 Frozen frames Duration
Before 55.4 2.6% 85.1ms 0 30s
After 59.0 0.6% 16.7ms 0 30s
Delta +3.6 -2.0pp -68.4ms 0 0s

Validated with the reproducible sample provided by a customer: 1st row is after, 2nd row is before

Simulator 2026-04-29 17 41 21

Replay Before: https://sentry-sdks.sentry.io/explore/replays/78440eab9df54aa1abfc2a670354b488

Replay After: https://sentry-sdks.sentry.io/explore/replays/277fbeaf481044a4a4d9017f54c85e00/

#skip-changelog

Closes #6885

Defer screenshots during active interactions and dense animations so replay capture avoids the hottest UI frames.

Back off the screenshot cadence when capture is slow, then recover toward the configured frame rate after faster captures.
@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 29, 2026

⚠️ JUnit XML file not found

The CLI was unable to find any JUnit XML files to upload.
For more help, visit our troubleshooting guide.

Drive replay captures from a default-mode run loop observer so screenshot work runs after UI work instead of from the display link.
activities,
true,
CFIndex.max,
{ _, activity, context in
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The first parameter in this callback should be the observer, you can access the mode property and check if it's .interactive. That's how you can avoid firing the capture logic if the user is scrolling

romtsn added 4 commits April 29, 2026 20:45
Observe replay capture scheduling in common modes and read the current run loop mode from the observer callback.

Skip capture bookkeeping while the run loop is in tracking mode so scrolling does not trigger screenshots.
Use the block-based run loop observer callback and keep replay capture timing state named around screenshot timestamps.

Flatten the deferral and adaptive interval branches without changing capture thresholds.
Keep tracking-mode suppression in the run loop scheduler and leave the capture guard focused on active interactions and animations.
@philprime
Copy link
Copy Markdown
Member

I quickly glanced over the changes and while I believe it to be viable (with the adoption of the existing HangTracker.swift) we need to do a full end-to-end verification with sample apps having a lot of tracked interactions, e.g. lots of scrolling, and compare it to sample apps without any activity in the UI.

My main concern is that we are not taking screenshots at a one second interval anymore, but instead the interval can be longer than that (less than a second is debounced).

In that case we need to look into two topics:

  1. Extrapolating screenshots to reach the one second interval (for 1 FPS), i.e. if the time between screenshot A and screenshot B is more than one second we need to duplicate screenshot A.
  2. Dynamic frame rates in MPEG containers, i.e. if the screenshots are taken in variable intervals larger than one second, we don't duplicate/extrapolate but instead the containers include information that the next frame will be in e.g. 2.4 seconds

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Low overhead session replay with runloop observers

3 participants