Add opt-in Skwasm multi-surface rasterizer#188628
Draft
MarlonJD wants to merge 3 commits into
Draft
Conversation
Resolve conflict in skwasm_impl/surface.dart: keep the PR's generalized CanvasProvider<DomEventTarget> / DomEventTarget canvas types for the onscreen SkwasmSurface, and incorporate master's new supportsPngEncoding => false override (now required by the Surface interface).
MarlonJD
force-pushed
the
skwasm-safari-retained-presentation
branch
from
June 27, 2026 16:14
3c7da4b to
1f6c738
Compare
This was referenced Jun 30, 2026
Closed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Related issue: #187660
Related PRs: #188614 (Skwasm artifact-variant test support), #188623 (cross-browser macrobench runner)
Motivation
The Safari/Firefox problem is not only that Skwasm is currently unsupported by policy. On
non-Chrome browsers, the existing full-frame ImageBitmap presentation path can be expensive and
brittle. This PR does not change the default renderer policy: the production gate
defaultWasmSupport = { blink: true, gecko: false, webkit: false }(
lib/web_ui/flutter_js/src/browser_environment.js, consumed atflutter_js/src/loader.js:82)keeps non-Chromium on CanvasKit by default and is left untouched. This PR adds a measurable
opt-in direct/multi-surface presentation path so browser behavior can be validated separately
from any policy decision.
What Changed
skwasmForceMultiSurfaceRasterizerJS config / environment flag.OffscreenCanvasRasterizerby default.MultiSurfaceRasterizerselection for Skwasm when the flag is enabled.SkwasmSurface.OffscreenCanvasfor multi-threaded Skwasm whenHTMLCanvasElement.transferControlToOffscreen()is available.HTMLCanvasElementfor single-threaded Skwasm.captureImageBitmapsparameter to the native render path so onscreen/direct presentation does not capture unnecessary ImageBitmaps.Static correctness (source-grounded, re-verifiable)
Verified read-only against the PR's multi-surface diff (unchanged by the master merge):
browserSupportsTransferControlToOffscreen(HTMLCanvasElement.prototype.transferControlToOffscreen != null), nevernavigator.userAgent.skwasmForceMultiSurfaceRasterizerdefaults to false (bool.fromEnvironment('FLUTTER_WEB_SKWASM_FORCE_MULTI_SURFACE_RASTERIZER')).OffscreenCanvasRasterizeron Chrome, Firefox, and Safari.MultiSurfaceRasterizeronly whentransferControlToOffscreenexists, and hands skwasm a transferredOffscreenCanvas— never a non-transferableHTMLCanvasElement.HTMLCanvasElement.7/7 opt-in invariants pass in source.
Non-goals
Evidence — re-verifiable local benchmark (2026-06-28)
Status remains
candidate-only; default enablement remainsNOT_READY. The numbers below arefreshly re-collected and re-verifiable (real GPU, this machine), superseding earlier
exploratory runs whose raw artifacts were lost.
Method. A complete same-engine web SDK (CanvasKit +
skwasm+skwasm_heavy) alreadyexisted in the local build output, so no engine rebuild was needed — it was served as-is. A
small harness serves the pre-built macrobenchmarks bundle with COOP/COEP (so
skwasm_heavygetscrossOriginIsolated/ SharedArrayBuffer), forces the renderer via the loader config, drivesheadful Chrome / Firefox / Safari (isolated temp profiles, real GPU), and collects the
app-reported
drawFrameDuration.average. No DevTools tracing — Chrome-only trace metrics areintentionally excluded. Every run records the actual artifact loaded (artifact proof).
3-pass mean
drawFrameDuration.average(µs, lower is better):skwasmskwasm_heavyskwasm_heavyskwasm_heavy~1.8–2.6×). This comes from the existing OffscreenCanvas/ImageBitmap path + theskwasm_heavyartifact — confirmed by artifact proof — not from the multi-surface code this PR adds. The loader'sautoheuristic already picksskwasm_heavyfor non-Chromium (no Chromium break iterators).bench_cardranged 1611–2025 across passes); Skwasm paths are stable (Safari skwasmbench_card≈0.6% spread).skwasm.wasmloaded) crashes on both Safari and Firefox —UnimplementedError: v8BreakIterator is not supportedduring layout (normal Skwasm depends on Chromium-onlyIntl.v8BreakIterator).skwasm_heavybundles its own break-iterator data and runs. So on non-Chromium browsersskwasm_heavyis required for correctness, not merely faster — which is exactly what the loader'sautoheuristic already selects.Important boundaries:
defaultWasmSupportgecko/webkitfalse) is untouched.InvalidStateErrorwhen resizing anHTMLCanvasElementaftertransferControlToOffscreen(). Classifiedcapability exists but behavior insufficient— not a WebKit bug.Verification
1f6c738f1fadc6a41157b778f8060d4662cb5f84(master-merge resolving the conflict; multi-surface diff unchanged).git diff --checkclean; the Static-correctness section above is re-runnable read-only against the PR diff.safaridriver); isolated temp profiles; 3 passes; artifact proof every run;drawFrameDuration.averageonly.Next Plan
InvalidStateErroris pinned tosurface.dartsetSize()->_canvasProvider.resizeCanvas(_canvas, size): on the transferred-canvas path,_canvasis theHTMLCanvasElementwhose control was already moved viatransferControlToOffscreen(), so resizing its backing store throws. Fix direction: when_useTransferredCanvas, apply only CSS size and leave backing-store sizing tosurfaceSetSize(...); add a unit test with a fake that throws on thewidth/heightsetter. Keep behind the opt-in/debug flag.Source-fix update (2026-06-30)
The resize issue called out in the previous Next Plan is now addressed in this PR by:
OnscreenCanvasProvider.resizeCanvasCss(...), which updates only the visible CSS size;surfaceSetSize(...);test/engine/compositing/canvas_provider_test.dartcoverage for both full backing-store resize and CSS-only resize at the evidence-sized1206 x 2142 @ DPR 3case.New commit on this PR head:
2edd50a01813cdb588d219b71eb0baf7b567f569—fix(web): keep transferred skwasm canvas CSS size currentAdditional local validation for this source-fix commit:
dart format ...for the three changed files.felt test --gcs-prod --browser chrome --compiler dart2js --renderer canvaskit --fail-early test/engine/compositing/canvas_provider_test.dart:chrome-dart2js-canvaskit-engine,2/2,All tests passed!felt test --gcs-prod --browser chrome --compiler dart2js --renderer canvaskit --fail-early test/engine/compositing/render_canvas_test.dart:chrome-dart2js-canvaskit-engine,2/2,All tests passed!MobileSafari evidence remains local and narrow: the source-fix candidate reached final connected canvas
402 x 714with backing attrs1206 x 2142in one Simulator benchmark and two physical iPhone Skwasm benchmarks, plus a physical iPhone CanvasKit control for the same candidate bundle. This does not change the default WebKit/Safari policy; default enablement remains out of scope for this PR.