Skip to content

[web] Add RenderCanvas sizing diagnostics#188724

Closed
MarlonJD wants to merge 1 commit into
flutter:masterfrom
MarlonJD:mobile-safari-skwasm-canvas-sizing-diagnostic
Closed

[web] Add RenderCanvas sizing diagnostics#188724
MarlonJD wants to merge 1 commit into
flutter:masterfrom
MarlonJD:mobile-safari-skwasm-canvas-sizing-diagnostic

Conversation

@MarlonJD

@MarlonJD MarlonJD commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

Draft status

Diagnostic-only draft for #188724.

This is not a WebKit default enablement PR. It does not flip webkit: true,
and it does not claim the Mobile Safari Skwasm canvas sizing issue is fixed.

What changed

Adds compile-time gated RenderCanvas sizing diagnostics behind:

FLUTTER_WEB_DEBUG_RENDER_CANVAS_SIZING

When enabled, the logs capture the values needed to separate the Mobile Safari
failure branches:

  • requested bitmap size
  • _pixelWidth / _pixelHeight
  • canvas width/height attributes
  • EngineFlutterDisplay.instance.devicePixelRatio
  • window.devicePixelRatio
  • visualViewport width/height/scale
  • documentElement.clientWidth/clientHeight
  • inline and computed canvas CSS width/height
  • canvas and host bounding rects
  • the same state on the next animation frame after logical-size writes and
    bitmap transfer

Why

On physical iPhone Mobile Safari, a Skwasm build can load skwasm_heavy,
main.dart.mjs, and main.dart.wasm, complete benchmark profiles, and produce
non-empty canvas pixels while the visible canvas CSS rect collapses to about
0.328125 x 0.328125px.

The same failing evidence class has a 1206 x 2142 backing store and a browser
viewport of about 402 x 714 at DPR 3. A local-only forced CSS probe that
sets canvas.style.width = canvas.width / devicePixelRatio and
canvas.style.height = canvas.height / devicePixelRatio recovers the visible
surface. The next step is to prove whether the bad value is a stale pixel size,
DPR/viewport timing, or a post-write CSS overwrite.

Refs issue #188723.

Validation

  • ./bin/dart format engine/src/flutter/lib/web_ui/lib/src/engine/compositing/render_canvas.dart
  • Targeted RenderCanvas invariant test with GCS artifacts:
DART_SDK_DIR=/Users/marlonjd/Developer/flutter/bin/cache/dart-sdk \
  engine/src/flutter/lib/web_ui/dev/felt test \
  --gcs-prod \
  --browser chrome \
  --compiler dart2js \
  --renderer canvaskit \
  --fail-early \
  test/engine/compositing/render_canvas_test.dart
  • Result: passed locally with --gcs-prod; chrome-dart2js-canvaskit-engine,
    3/3 tests.
  • Local SDK build setup:
PATH=/Users/marlonjd/Developer/demo/skwasm-pr-188628/.tooling/depot_tools:$PATH \
DART_SDK_DIR=/Users/marlonjd/Developer/demo/skwasm-pr-188628/flutter-pr-worktree/bin/cache/dart-sdk \
PUB_CACHE=/Users/marlonjd/Developer/demo/skwasm-pr-188628/.pub-cache \
  engine/src/flutter/lib/web_ui/dev/felt build sdk canvaskit skwasm skwasm_heavy
  • Result: passed locally for SDK/artifact production; local outputs include
    flutter_web_sdk/flutter_js/flutter.js and
    canvaskit/{canvaskit,skwasm,skwasm_heavy}.{js,wasm}.

Diagnostic runtime follow-up

The first local rebuilt SDK diagnostic/no-log Simulator bundles were blocked
before canvas creation with:

RuntimeError: Unreachable code should not be executed

from canvaskit/skwasm_heavy.js. Those runs had profiles: [],
renderCanvasSizingLogs=[], and did not reach the intended canvas sizing logs.

Controls narrowed that blocker:

  • safe/no-log local rebuilt SDK controls still hit the same runtime error.
  • swapping only cached skwasm_heavy.wasm into the local rebuilt bundle did
    not recover the run.
  • full cached-SDK control completed all three profiles with errors: [] and
    normal 402 x 714 canvas geometry on the same Simulator.
  • cached full canvaskit/ did not rescue local main.dart.wasm /
    main.dart.mjs.
  • cached main output completed with the local rebuilt full canvaskit/
    directory.
  • removing only the RenderCanvas diagnostic source patch changed app wasm
    contents but left the local main.dart.mjs support-runtime shape unchanged.
  • the local --local-web-sdk path invoked engine-prebuilt Dart 3.12.1 with
    kernel format 130, while the passing cached-SDK path used Dart
    3.13.0-254.0.dev with kernel format 136.

After aligning the local engine-prebuilt Dart SDK symlinks to the cached
3.13.0-254.0.dev Dart SDK and rebuilding the local platform dill set, the
aligned local diagnostic bundle moved to the cached-like dart2wasm support
runtime shape and reached RenderCanvas logs.

Aligned diagnostic bundle:

diagnostic_bundles/20260630T_aligned_dart_sdk_local_web_sdk_diagnostic/bundle

Simulator MobileSafari one-benchmark run:

iphone_evidence_runs/20260630T020038_simulator_skwasm_aligned_dart_sdk_diagnostic_one_benchmark.json
  • Result: all-benchmarks-complete
  • Loaded artifacts: skwasm_heavy, main.dart.mjs, main.dart.wasm
  • Profiles: one bench_card_infinite_scroll profile
  • Errors: []
  • RenderCanvas sizing log count: 1813
  • Representative log after sizing:
    requested=1206x2142, pixel=1206x2142, canvasAttr=1206.0x2142.0,
    [email protected], inlineStyle=402px/714px,
    computedStyle=402px/714px, [email protected],0.0

Physical iPhone MobileSafari one-benchmark run:

iphone_evidence_runs/20260630T020352_physical_iphone_skwasm_aligned_dart_sdk_diagnostic_one_benchmark.json
  • Result: all-benchmarks-complete
  • Loaded artifacts: skwasm_heavy, main.dart.mjs, main.dart.wasm
  • Profiles: one bench_card_infinite_scroll profile
  • Errors: []
  • RenderCanvas sizing log count: 1807
  • Representative log after sizing:
    requested=1206x2142, pixel=1206x2142, canvasAttr=1206.0x2142.0,
    [email protected], inlineStyle=402px/714px,
    computedStyle=402px/714px, [email protected],0.0
  • Visual note: the local operator reported that the full scroll content was
    visible during the physical run. An automated physical screenshot is still
    separate evidence and was not captured in this one-benchmark run.

Physical iPhone manual-screenshot follow-up:

iphone_evidence_runs/20260630T021605_physical_iphone_skwasm_aligned_dart_sdk_diagnostic_manual_screenshot_window.json
  • Result: all-benchmarks-complete
  • Loaded artifacts: skwasm_heavy, main.dart.mjs, main.dart.wasm
  • Profiles: all three macrobenchmarks completed
  • Errors: []
  • RenderCanvas sizing log count: 5421
  • Representative log after sizing:
    requested=1206x2142, pixel=1206x2142, canvasAttr=1206.0x2142.0,
    [email protected], inlineStyle=402px/714px,
    computedStyle=402px/714px, [email protected],0.0
  • Manual screenshots captured from physical iPhone MobileSafari show visible
    benchmark content for the card, lazy text, and wrapbox scroll benchmarks:
    20260630T021605_physical_iphone_skwasm_aligned_dart_sdk_diagnostic_manual_screenshot_window_card_scroll.png,
    20260630T021605_physical_iphone_skwasm_aligned_dart_sdk_diagnostic_manual_screenshot_window_text_scroll.png,
    20260630T021605_physical_iphone_skwasm_aligned_dart_sdk_diagnostic_manual_screenshot_window_wrapbox_scroll.png.

RenderCanvas log-vs-collapse analysis:

diagnostic_bundles/20260630T_render_canvas_log_vs_collapse_analysis/render_canvas_log_vs_collapse_analysis.md
  • Collapsed physical/simulator snapshots had valid 1206 x 2142 backing store
    and non-empty pixels, but inline/computed CSS and canvas rect collapsed to
    0.328125px / 0.328125px.
  • Aligned diagnostic logs show _ensureSize setting 1206 x 2142 canvas
    attributes, then updateLogicalSize:after writing 402px / 714px, with
    next-frame/transfer logs retaining 402 x 714.
  • Narrowed boundary: after _ensureSize has a valid backing store and
    before/at _updateLogicalHtmlCanvasSize() logical CSS write, or a later
    overwrite of that write.

Copied collapsed-bundle style observer follow-up:

iphone_evidence_runs/20260630T024955_simulator_collapsed_bundle_canvas_style_observer_inline_layout_one_benchmark.json
  • Bundle:
    diagnostic_bundles/20260629T1735_collapsed_skwasm_ready_rerun_bundle/bundle
  • Result: all-benchmarks-complete
  • Loaded artifacts: skwasm_heavy, main.dart.mjs, main.dart.wasm
  • Profiles: one bench_card_infinite_scroll profile
  • Errors: []
  • Style write events: 642
  • Canvas style snapshots: 32
  • Key finding: the canvas itself receives inline CSS
    0.333333px / 0.333333px; the connected computed/rect value is
    0.328125 x 0.328125; the backing store later reaches 1206 x 2142, but
    the canvas CSS size is not corrected to 402 x 714.
  • The repeated 402.0px / 714.0px writes in this trace appear to belong to
    outer flutter-view sizing, not the canvas element.

Analysis artifact:

diagnostic_bundles/20260630T_canvas_style_observer_trace_analysis/canvas_style_observer_analysis.md

Boundary: the diagnostic logging blocker is now narrowed to local Dart
compiler/platform-kernel/support-runtime drift and cleared for the aligned
local diagnostic control. The copied collapsed-bundle observer narrows the
remaining source question further: why does the canvas keep an early 1 / DPR
logical CSS size after the backing store is resized? This does not fix the
original Mobile Safari canvas sizing bug, and it is not default WebKit/Safari
enablement evidence.

@github-actions github-actions Bot added engine flutter/engine related. See also e: labels. platform-web Web applications specifically team-web Owned by Web platform team labels Jun 29, 2026
@MarlonJD MarlonJD closed this Jul 10, 2026
@MarlonJD

Copy link
Copy Markdown
Contributor Author

Superseded by #188797, which landed the production fix; #188723 is now closed. Closing this diagnostic-only draft

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

Labels

engine flutter/engine related. See also e: labels. platform-web Web applications specifically team-web Owned by Web platform team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant