Skip to content

[web] Unify ui.Canvas behind a shared EngineCanvas frontend - #196348

Merged
auto-submit[bot] merged 11 commits into
flutter:masterfrom
harryterkelsen:unify-ui-canvas-across-canvaskit-and-skwasm
Nov 13, 2026
Merged

auto-submit[bot] merged 11 commits into
flutter:masterfrom
harryterkelsen:unify-ui-canvas-across-canvaskit-and-skwasm

Conversation

@harryterkelsen

Copy link
Copy Markdown
Contributor

Unifies ui.Canvas across CanvasKit and Skwasm behind a shared EngineCanvas frontend (primitives/canvas.dart) and stateless BackendCanvas boundary (backend/canvas.dart), turning CkCanvas and SkwasmCanvas into thin native translators. Centralizing argument validation, PictureImageTracker image retention, the per-draw BackendPaint lifecycle, point/atlas typed-array flattening, superellipse path emulation, getLocalClipBounds, and WebParagraph dispatch brings all web renderers onto a single implementation with consistent runtime behavior.

Part of #175630

Behavioral Changes

  • Skwasm and Web Impeller now validate Canvas arguments identically to CanvasKit and native dart:ui:
    • Before: Skwasm silently truncated, misread, or passed mismatched buffer lengths to native code when given a transform matrix without 16 entries, an odd-length drawRawPoints list, or mismatched drawAtlas/drawRawAtlas transform, rect, and color lengths.
    • After: Each throws the same ArgumentError and message as CanvasKit and native dart:ui, without mutating the save stack, transform, or tracked images.
  • Consistent debug assertions across all web renderers:
    • Skwasm and Web Impeller now assert on NaN rects, rounded rects, rounded superellipses, and offsets (including clipRSuperellipse/drawRSuperellipse on CanvasKit as well).
    • Drawing a disposed Image, Picture, Paragraph, Vertices, or atlas image, or calling drawAtlas/drawRawAtlas with colors and a null blendMode, now throws AssertionError across all web renderers (previously missing, or throwing StateError for disposed pictures on CanvasKit and disposed vertices).
  • drawAtlas(colors: []) on Skwasm now treats an empty color list as "no colors":
    • Before: Skwasm passed a zero-length buffer and native code read one uninitialized color per sprite.
    • After: Matches CanvasKit and native dart:ui by passing null when colors is empty.
  • drawParagraph now supports WebParagraph on Skwasm:
    • Before: SkwasmCanvas.drawParagraph threw a TypeError when given a WebParagraph; only CanvasKit routed it to WebParagraph.paint.
    • After: EngineCanvas.drawParagraph dispatches WebParagraph on every renderer.

Where to focus

  • lib/src/engine/primitives/canvas.dart: Ordering invariant in EngineCanvas—all validation and throwing delegate unwraps (backendPath, backendImage, EngineVertices.delegate, getBackendFilter) run before _tracker recording and createBackendPaint(), so rejected calls never retain images or leak a native paint. Image and atlas draws pass defaultBlurTileMode: ui.TileMode.clamp; all other draws use the default decal mode.
  • lib/src/engine/canvaskit/canvas.dart and lib/src/engine/skwasm/skwasm_impl/canvas.dart: Stateless BackendCanvas translators preserving backend-specific conversions (CanvasKit degree conversion, Mitchell cubic sampling, and float nine-slice centers; Skwasm StackScope conversions, integer nine-slice centers, and drawAtlas cullRect).
  • lib/src/engine/canvaskit/{picture_recorder,picture,surface}.dart and lib/src/engine/skwasm/skwasm_impl/picture.dart: Recorders and internal raster canvases now wrap their backend canvas in EngineCanvas, and LayerPicture exposes imageTracker so EngineCanvas.drawPicture merges retained image sources in the frontend.

Pre-launch Checklist

If you need help, consider asking for advice on the #hackers-new channel on Discord.

If this change needs to override an active code freeze, provide a comment explaining why. The code freeze workflow can be overridden by code reviewers. See pinned issues for any active code freezes with guidance.

Note: The Flutter team is currently trialing the use of Gemini Code Assist for GitHub. Comments from the gemini-code-assist bot should not be taken as authoritative feedback from the Flutter team. If you find its comments useful you can update your code accordingly, but if you are unsure or disagree with the feedback, please feel free to wait for a Flutter team member's review for guidance on which automated comments should be addressed.

Adds EngineCanvas, a ui.Canvas implementation shared by all renderers,
over a new stateless BackendCanvas boundary. EngineCanvas owns the
argument checks (matching native dart:ui), image retention, the per-draw
paint lifecycle and unwrapping of frontend objects. CkCanvas and
SkwasmCanvas become thin BackendCanvas translators.

Skwasm and wimp now get the same argument checks as native, empty
drawAtlas colors are treated as no colors, and WebParagraph can be drawn
on every renderer.
…etained by every draw, empty point and atlas lists, and local clip bounds under rotation and singular transforms
@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 Nov 11, 2026
@harryterkelsen
harryterkelsen marked this pull request as ready for review November 11, 2026 00:44
@harryterkelsen harryterkelsen added the CICD Run CI/CD label Nov 11, 2026

@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 the web engine's canvas architecture by introducing a unified EngineCanvas frontend that implements ui.Canvas and handles argument validation, image tracking, and paint lifecycles. It delegates rendering operations to stateless backend implementations (CkCanvas and SkwasmCanvas) via a new BackendCanvas interface. The review feedback suggests optimizing drawPoints, drawRawPoints, drawAtlas, and drawRawAtlas in EngineCanvas by returning early when the input collections are empty to avoid unnecessary allocations and setup.

@harryterkelsen harryterkelsen added the autosubmit Merge PR when tree becomes green via auto submit App label Nov 12, 2026
@auto-submit
auto-submit Bot added this pull request to the merge queue Nov 13, 2026
Merged via the queue into flutter:master with commit 6b2bb92 Nov 13, 2026
22 checks passed
@flutter-dashboard flutter-dashboard Bot removed the autosubmit Merge PR when tree becomes green via auto submit App label Nov 13, 2026
auto-submit Bot pushed a commit to flutter/packages that referenced this pull request Nov 13, 2026
…13653)

Manual roll Flutter from 9c427b5d7b1c to 1b9585481095 (32 revisions)

Manual roll requested by [email protected]

flutter/flutter@9c427b5...1b95854

2026-11-13 [email protected] Roll pub packages (flutter/flutter#196541)
2026-11-13 [email protected] Do not route PRs for review without cla, convert to draft (flutter/flutter#196341)
2026-11-13 [email protected] Roll Skia from d87a6c04da74 to 11f46cfe8838 (13 revisions) (flutter/flutter#196533)
2026-11-13 [email protected] [flutter_tools, flutter_test] Deprecate --experimental-faster-testing (flutter/flutter#196123)
2026-11-13 [email protected] [flutter_tools] Handle RPCError in ScreenshotCommand.runSkia (flutter/flutter#196132)
2026-11-13 [email protected] Roll Dart SDK from a8b961fd48bb to c91e8b6674e2 (2 revisions) (flutter/flutter#196502)
2026-11-13 [email protected] Roll Skia from 0c5ce4960cef to d87a6c04da74 (12 revisions) (flutter/flutter#196497)
2026-11-13 [email protected] Don't call `skia_image()`  for raster-owned images in `DlImage::Equals` (flutter/flutter#196473)
2026-11-13 [email protected] [flutter_tools] Require FlutterCommandRunner DI fields and extract findCommandName (flutter/flutter#196331)
2026-11-13 [email protected] [web] Unify ui.Canvas behind a shared EngineCanvas frontend (flutter/flutter#196348)
2026-11-13 [email protected] Link to MediaQuery.displayFeaturesOf from DisplayFeatures (flutter/flutter#193728)
2026-11-13 [email protected] Removes reference to unnecessary ensureSemantics call in doc (flutter/flutter#195831)
2026-11-13 [email protected] [flutter_tools] Register trackWidgetCreation in AppleBuildOptionsBundle (flutter/flutter#196078)
2026-11-12 [email protected] [Android] Skip the HardwareBuffer defect fallback for platform views (flutter/flutter#193441)
2026-11-12 [email protected] Fix supports_decal check in framebuffer_blend.frag (flutter/flutter#196227)
2026-11-12 [email protected] [ios] Update the screen in didUpdateEffectiveGeometry (flutter/flutter#195508)
2026-11-12 [email protected] [flutter_tools] Add `Cache-Control` headers for web test assets (flutter/flutter#196414)
2026-11-12 [email protected] Roll Skia from 3b71ec08218f to 0c5ce4960cef (16 revisions) (flutter/flutter#196472)
2026-11-12 [email protected] [web] Render bounded ImageFilter.blur on wimp (flutter/flutter#196081)
2026-11-12 [email protected] Roll Dart SDK from 69567bd3c427 to a8b961fd48bb (2 revisions) (flutter/flutter#196464)
2026-11-12 [email protected] [engine] Release the Skia OpenGL resource context on shutdown (flutter/flutter#193592)
2026-11-12 [email protected] [web] Fix decoding of still AVIF images on Chrome (flutter/flutter#195958)
2026-11-12 [email protected] Gate SurfaceControl context setting on API 34+ (flutter/flutter#196394)
2026-11-12 [email protected] [flutter_tools] Only pass on a valid root tool in DASH__TOOL (flutter/flutter#196228)
2026-11-12 [email protected] Support GL_TEXTURE_EXTERNAL_OES embedder textures in Impeller (flutter/flutter#195857)
2026-11-12 [email protected] Stop building linux/wasm_release in local_engine_builds (flutter/flutter#196345)
2026-11-12 [email protected] Split flame out of Linux customer_testing (flutter/flutter#196347)
2026-11-12 Jiwon Lee [Windows] Fix Space input after Alt+Space with previous Microsoft IME (flutter/flutter#192105)
2026-11-12 [email protected] [flutter_tools] Add ToolExtensionException, exit() guard, graceful shutdown, and base modifiers (flutter/flutter#195908)
2026-11-12 [email protected] Roll Packages from 653628e to 14f9933 (9 revisions) (flutter/flutter#196450)
2026-11-12 [email protected] Roll Dart SDK from e5add7a25b32 to 69567bd3c427 (2 revisions) (flutter/flutter#196437)
2026-11-12 [email protected] Roll Skia from 9aa2513a4ac9 to 3b71ec08218f (1 revision) (flutter/flutter#196436)

If this roll has caused a breakage, revert this CL and set the roller
to dry run mode using the controls here:
https://autoroll.skia.org/r/flutter-packages
Please CC [email protected],[email protected] on the revert to ensure that a human
is aware of the problem.

To file a bug in Packages: https://github.com/flutter/flutter/issues/new/choose

To report a problem with the AutoRoller itself, please file a bug:
https://issues.skia.org/issues/new?component=1389291&template=1850622

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

Labels

CICD Run CI/CD 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.

2 participants