Repository navigation
[web] Make resize and render atomic on the shared offscreen surface - #194401
Merged
auto-submit[bot] merged 4 commits intoOct 19, 2026
Merged
Conversation
mdebbar
marked this pull request as ready for review
October 16, 2026 18:47
Contributor
There was a problem hiding this comment.
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.
harryterkelsen
previously approved these changes
Oct 16, 2026
harryterkelsen
left a comment
Contributor
There was a problem hiding this comment.
LGTM with some nits
harryterkelsen
approved these changes
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
This was referenced Oct 20, 2026
This was referenced Oct 21, 2026
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
2 of 5 tasks
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`.
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.
Alternative to #185622.
#179473 split rendering on the shared
OffscreenSurfaceinto two separately awaited steps:setSize()and thenrasterizeToImageBitmaps(). 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'sImage.toByteDatauses 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:
createImageBitmap()run synchronously.Fixes #185034