Skip to content

[web] Make resize and render atomic on the shared offscreen surface - #194401

Merged
auto-submit[bot] merged 4 commits into
flutter:masterfrom
mdebbar:multiview-atomic-offscreen-surface
Oct 19, 2026
Merged

auto-submit[bot] merged 4 commits into
flutter:masterfrom
mdebbar:multiview-atomic-offscreen-surface

Conversation

@mdebbar

@mdebbar mdebbar commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

Alternative to #185622.

#179473 split rendering on the shared OffscreenSurface into two separately awaited steps: setSize() and then rasterizeToImageBitmaps(). When multiple views render at the same time, one view can resize the surface between another view's resize and render, so that view ends up with a bitmap of the wrong size. Skwasm's Image.toByteData uses the same surface and has the same problem.

Instead of using a lock (like I did here), the size now travels with each render, so the surface resizes and renders as one indivisible operation:

Fixes #185034

@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 Oct 14, 2026
@mdebbar mdebbar added the CICD Run CI/CD label Oct 15, 2026
@mdebbar
mdebbar marked this pull request as ready for review October 16, 2026 18:47

@gemini-code-assist gemini-code-assist Bot left a comment

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.

Code Review

This pull request refactors offscreen surface rasterization to make resizing and rendering atomic operations, preventing race conditions when a single offscreen surface is shared across multiple views. It updates OffscreenSurface APIs to accept a BitmapSize parameter, propagates these changes to the CanvasKit and Skwasm backends, and adds a comprehensive multi-view sizing test suite. Feedback on the C++ Skwasm implementation highlights a critical issue where ResizeSurface is called before making the OpenGL/WebGL context current via Skwasm::makeCurrent, which could lead to rendering corruption or crashes.

Comment thread engine/src/flutter/skwasm/surface.cc Resolved
Comment thread engine/src/flutter/skwasm/surface.cc Resolved
harryterkelsen
harryterkelsen previously approved these changes Oct 16, 2026

@harryterkelsen harryterkelsen left a comment

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.

LGTM with some nits

@mdebbar mdebbar added the autosubmit Merge PR when tree becomes green via auto submit App label Oct 19, 2026
@auto-submit
auto-submit Bot added this pull request to the merge queue Oct 19, 2026
Merged via the queue into flutter:master with commit 5ff0c16 Oct 19, 2026
23 checks passed
@flutter-dashboard flutter-dashboard Bot removed the autosubmit Merge PR when tree becomes green via auto submit App label Oct 19, 2026
hannah-hyj pushed a commit to hannah-hyj/flutter that referenced this pull request Oct 19, 2026
…lutter#194401)

Alternative to flutter#185622.

flutter#179473 split rendering on the
shared `OffscreenSurface` into two separately awaited steps: `setSize()`
and then `rasterizeToImageBitmaps()`. When multiple views render at the
same time, one view can resize the surface between another view's resize
and render, so that view ends up with a bitmap of the wrong size.
Skwasm's `Image.toByteData` uses the same surface and has the same
problem.

Instead of using a lock (like I did
[here](flutter#185622)), the size now
travels with each render, so the surface resizes and renders as one
indivisible operation:
- Skwasm: the size is part of the same message to the raster thread (as
it was before flutter#179473). Messages
are processed in order, so no locking is needed, and there is one less
worker round trip per view per frame.
- CanvasKit: resize, draw, and `createImageBitmap()` run synchronously.

Fixes flutter#185034
infinitedim added a commit to infinitedim/justui that referenced this pull request Oct 25, 2026
…rface race

Flutter 3.47 rasterizes all views through one offscreen surface, and a
view can resize it for itself before another view renders, which cut
off every view larger than the last one sized (flutter/flutter#194401,
fixed on master, not in stable). Views are now 320 x 344 everywhere,
centered in the part box and clipped to it, so the race is harmless.
docs-web.md states the new host contract.
suryaKommana2662 pushed a commit to suryaKommana2662/flutter that referenced this pull request Oct 29, 2026
…lutter#194401)

Alternative to flutter#185622.

flutter#179473 split rendering on the
shared `OffscreenSurface` into two separately awaited steps: `setSize()`
and then `rasterizeToImageBitmaps()`. When multiple views render at the
same time, one view can resize the surface between another view's resize
and render, so that view ends up with a bitmap of the wrong size.
Skwasm's `Image.toByteData` uses the same surface and has the same
problem.

Instead of using a lock (like I did
[here](flutter#185622)), the size now
travels with each render, so the surface resizes and renders as one
indivisible operation:
- Skwasm: the size is part of the same message to the raster thread (as
it was before flutter#179473). Messages
are processed in order, so no locking is needed, and there is one less
worker round trip per view per frame.
- CanvasKit: resize, draw, and `createImageBitmap()` run synchronously.

Fixes flutter#185034
@mdebbar mdebbar added the cp: beta cherry pick this pull request to beta release candidate branch label Nov 2, 2026
auto-submit Bot pushed a commit that referenced this pull request Nov 3, 2026
…surface (#195839)

This pull request is created by [automatic cherry pick workflow](https://github.com/flutter/flutter/blob/main/docs/releases/Flutter-Cherrypick-Process.md#automatically-creates-a-cherry-pick-request)

Cherry-pick of #194401.

### Issue Link:
#185034

### Impact Description:
In multi-view Flutter Web apps, views rendered at the same time race on a shared offscreen surface and end up with the wrong canvas size, clipping or overflowing their host HTML containers. Skwasm's `Image.toByteData` uses the same surface and has the same problem.

This is a regression since Flutter 3.41 (#179473) that affects deployed multi-view web apps on both CanvasKit and Skwasm and has been blocking production apps from upgrading past 3.38.

### Changelog Description:
[flutter/185034](#185034): When rendering multiple views on the web, canvases may fail to match their host container dimensions.

### Workaround:
None.

### Risk:
What is the risk level of this cherry-pick?

### Test Coverage:
Are you confident that your fix is well-tested by automated tests?

### Validation Steps:
Follow the repro steps in #185034, or run the new tests in `engine/src/flutter/lib/web_ui/test/ui/multi_view_sizing_test.dart`.
GhagSagar23 pushed a commit to GhagSagar23/flutter that referenced this pull request Nov 6, 2026
…utter#196052)

Follow-up to flutter#194401.


[`RenderView.compositeFrame`](https://github.com/flutter/flutter/blob/3623d2d34543adb1b340510eb5e7bfc0ae1aa1ae/packages/flutter/lib/src/rendering/view.dart#L352-L381)
passes the rendered scene's physical `size` to
`FlutterView.render(scene, size: ...)`. On the web,
`EngineFlutterView.render` resized `<flutter-view>` to `size` via
`handleFrameworkResize(size)` and then discarded `size`, leaving
`ViewRasterizer.draw` to rasterize at `view.physicalSize` (which
[`CustomElementDimensionsProvider.computePhysicalSize`](https://github.com/flutter/flutter/blob/3623d2d34543adb1b340510eb5e7bfc0ae1aa1ae/engine/src/flutter/lib/web_ui/lib/src/engine/view_embedder/dimensions_provider/custom_element_dimensions_provider.dart#L88-L96)
reads from `_hostElement.clientWidth` and `clientHeight`). When a
multi-view embedder configures `viewConstraints` larger than a
CSS-constrained `hostElement`, `<flutter-view>` expanded to `size` while
the `<canvas>` was rasterized and clipped at `hostElement`'s dimensions.

This PR threads `size` from `EngineFlutterView.render` into
`EnginePlatformDispatcher.render` (falling back to `target.physicalSize`
when `size` is omitted, matching native
[`FlutterView.render`](https://github.com/flutter/flutter/blob/3623d2d34543adb1b340510eb5e7bfc0ae1aa1ae/engine/src/flutter/lib/ui/window.dart#L380-L387))
and forwards the resolved frame size through `Renderer.renderScene`
(`RenderRequest`) and `ViewRasterizer.draw`.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CICD Run CI/CD cp: beta cherry pick this pull request to beta release candidate branch 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.

[web] Multi-view sizing regression: canvases fail to match host container dimensions based on last view added

2 participants