Skip to content

fix(agent): RenderRepaintBoundary screenshot for iOS/Impeller#108

Merged
pbertsch merged 2 commits into
mainfrom
fix/ios-sim-screenshot-impeller
May 13, 2026
Merged

fix(agent): RenderRepaintBoundary screenshot for iOS/Impeller#108
pbertsch merged 2 commits into
mainfrom
fix/ios-sim-screenshot-impeller

Conversation

@pbertsch
Copy link
Copy Markdown
Contributor

Problem

On Flutter 3.10+ with Impeller (the default renderer on iOS), OffsetLayer.toImage() returns a GPU-backed texture. Calling image.toByteData(format: png) on a GPU texture returns null in Impeller, which hit the bytes! null assertion in _screenshot() and crashed.

Result: all take screenshot steps and on failure auto-screenshots silently failed (or threw) on any iOS simulator running Flutter 3.10+.

Fix

RenderRepaintBoundary.toImage() is explicitly supported by Impeller — it triggers a readback from the GPU texture to CPU memory. Strategy:

  1. await endOfFrame before capture so the latest rendered frame is visible
  2. Primary: walk the element tree for the largest RenderRepaintBoundary (Impeller-safe)
  3. Fallback: OffsetLayer.toImage() (Skia, pre-3.10 builds)
  4. Null-safe: bytes nullable → surfaces as ProbeError instead of assertion crash

Testing

  • dart analyze lib/src/executor.dart — no issues
  • flutter test — 13/13 pass
  • Manually verified on iPhone 17 Pro Max simulator (Flutter 3.41.5, Impeller)

OffsetLayer.toImage() returns a GPU-backed texture on Impeller (Flutter 3.10+
default on iOS) where image.toByteData(png) returns null, causing a null
assertion crash. RenderRepaintBoundary.toImage() is the Impeller-safe path.

New strategy:
- endOfFrame wait before capture to ensure latest frame is rendered
- Primary: walk element tree for largest RenderRepaintBoundary (Impeller-safe)
- Fallback: OffsetLayer.toImage() (Skia compatibility, pre-3.10 builds)
- Null-safe: no more ! assertion on bytes, surfaces as a ProbeError instead
@pbertsch pbertsch requested a review from a team as a code owner May 13, 2026 00:04
Adds the probe_agent CHANGELOG entry (CI was blocking the merge of #108
because probe_agent/lib/ changed without a matching CHANGELOG update),
bumps all package versions from 0.9.4 to 0.9.5, and adds the matching
0.9.5 entries to the root CHANGELOG, wiki Home.md, mcp.md verify
snippet, and per-package CHANGELOGs.

The Dart agent code fix itself is in commit a595343 (already on this
PR branch):
  - Primary capture via the largest visible RenderRepaintBoundary,
    which Impeller supports natively (vs OffsetLayer.toImage which
    returns a GPU texture on Impeller and yields null PNG bytes).
  - Fallback to OffsetLayer.toImage when no boundary is found (Skia).
  - Awaits WidgetsBinding.instance.endOfFrame before capture.
  - Uses View.devicePixelRatio instead of hardcoded 2.0.

Verified:
  go test ./...   16/16 packages pass
  staticcheck ./... zero issues
  dart analyze probe_agent/lib   no issues found
@pbertsch pbertsch merged commit 8187d54 into main May 13, 2026
15 checks passed
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.

1 participant