Skip to content

[web_ui] Optimize skwasm text layout and path decoding to eliminate dynamic boxing churn under Wasm#186978

Merged
auto-submit[bot] merged 11 commits into
flutter:masterfrom
kevmoo:i186972_smarter_wasm_gc
May 29, 2026
Merged

[web_ui] Optimize skwasm text layout and path decoding to eliminate dynamic boxing churn under Wasm#186978
auto-submit[bot] merged 11 commits into
flutter:masterfrom
kevmoo:i186972_smarter_wasm_gc

Conversation

@kevmoo
Copy link
Copy Markdown
Contributor

@kevmoo kevmoo commented May 22, 2026

Avoids heap allocations and GC sweeps caused by dynamic boxing of primitive integers (struct allocations) inside high-frequency paragraph layout, segmenter, and line-breaking loops.

  1. Optimized Local Typed List Copy Loops:

    • Replaced generic closure parameters and generic List<int>.generate copy loops inside paragraph.dart and path.dart with specialized local pointer extensions (toUint8List / toUint32List).
    • Standard FFI Pointer.asTypedList is not supported at runtime under dart2wasm in the Web Engine's external memory layout (since FFI allocations are mapped to Skwasm's C++ heap rather than the standard Dart heap, leaving the global FFI helper null at runtime).
    • While these custom extensions still perform a memory copy step, they allocate concrete, specialized typed arrays (Uint8List / Uint32List) and copy elements using unboxed, register-level pointer lookups.
    • Loops are structured to iterate backwards (length - 1 down to 0). Compiling this to WebAssembly translates the loop condition to a direct comparison against a constant zero register (i64.ge_s to i64.const 0), eliminating a register load instruction (local.get $length) inside the high-frequency loop body on every iteration.
    • Under dart2wasm, the copy loops are fully inlined and compile to raw WebAssembly array loads and stores (i32.load / i32.store) with 0 generic closure callbacks and 0 dynamic integer boxing ($BoxedInt heap allocations).
    • For signed Pointer<Int8> buffers (such as in path.dart's toSvgString), the copy block explicitly masks values with & 0xFF to convert them to positive unsigned bytes ($0$ to $255$), preventing runtime FormatException issues under utf8.decode at zero memory overhead.
  2. Pattern-Based Switch Expression Comparisons:

    • Replaced generic Set<int> lookup collections (_kNewlines, _kSpaces) inside breakLinesUsingV8BreakIterator with pattern-matching switch expressions (_isNewline, _isSpace).
    • Under dart2wasm, this completely eliminates the dynamic boxing of checked primitive codeUnit integers into heap-allocated $BoxedInt wrappers (originally required for generic Set.contains lookup parameters).
    • Because the Unicode newline case numbers are sparse, the compiler lowers the switch statement to inline, register-level direct comparisons and conditional jumps (i64.eq and br_if) rather than an indirect jump table (br_table), avoiding binary size bloat while executing entirely on CPU registers with 0 heap allocations.

Fixes #186972

…ynamic boxing churn under Wasm

Avoids heap allocations and GC sweeps caused by dynamic boxing of primitive integers (struct allocations) inside high-frequency paragraph layout, segmenter, and line-breaking loops.

1. Added zero-allocation conversion extensions to raw_memory.dart:
   - Pointer<Uint8>.asUint8List
   - Pointer<Int8>.asUint8List (with explicit bitwise masking & 0xFF to guarantee signed-to-unsigned range conversion)
   - Pointer<Uint32>.asUint32List
   These extensions copy native array buffers directly into unboxed Dart typed list classes via flat, zero-allocation loops.

2. Optimized skwasm paragraph.dart:
   - Replaced generic closure List<int>.generate loop in _addSegmenterData with clean Pointer.asUint8List extension.
   - Replaced generic List<int>.generate loop in missing font code point search with codePointBuffer.asUint32List.

3. Optimized skwasm path.dart:
   - Replaced generic List<int>.generate loop inside toSvgString with buffer.asUint8List.

4. Optimized line_breaker.dart:
   - Replaced generic Set<int> lookups (_kNewlines, _kSpaces) inside breakLinesUsingV8BreakIterator with top-level pattern-based switch expressions (_isNewline, _isSpace). These compile directly to raw WebAssembly control-flow jump tables operating on unboxed registers.

Fixes flutter#186972
@flutter-dashboard flutter-dashboard Bot added the CICD Run CI/CD label May 22, 2026
@flutter-dashboard
Copy link
Copy Markdown

It looks like this pull request may not have tests. Please make sure to add tests or get an explicit test exemption before merging.

If you are not sure if you need tests, consider this rule of thumb: the purpose of a test is to make sure someone doesn't accidentally revert the fix. Ask yourself, is there anything in your PR that you feel it is important we not accidentally revert back to how it was before your fix?

Reviewers: Read the Tree Hygiene page and make sure this patch meets those guidelines before LGTMing. If you believe this PR qualifies for a test exemption, contact "@test-exemption-reviewer" in the #hackers channel in Discord (don't just cc them here, they won't see it!). The test exemption team is a small volunteer group, so all reviewers should feel empowered to ask for tests, without delegating that responsibility entirely to the test exemption group.

@github-actions github-actions Bot added engine flutter/engine related. See also e: labels. platform-web Web applications specifically labels May 22, 2026
@kevmoo

This comment was marked as outdated.

Copy link
Copy Markdown
Contributor

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

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 optimizes memory operations in the web engine by introducing pointer extensions for typed list conversion and replaces set-based lookups with switch expressions in the line breaker to prevent integer boxing in dart2wasm. Feedback was provided regarding missing documentation for the new public extensions, as required by the repository style guide.

@github-actions github-actions Bot removed the CICD Run CI/CD label May 22, 2026
@kevmoo
Copy link
Copy Markdown
Contributor Author

kevmoo commented May 22, 2026

/gemini review

Copy link
Copy Markdown
Contributor

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

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 replaces List<int>.generate and Set<int> lookups with specialized pointer extensions and switch expressions to avoid integer boxing in dart2wasm. Feedback focuses on adhering to Dart naming conventions by using the to... prefix for methods that create copies, using /// for all documentation comments per the Flutter style guide, and optimizing data copies using setRange with asTypedList.

Comment thread engine/src/flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/paragraph.dart Outdated
Comment thread engine/src/flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/paragraph.dart Outdated
Comment thread engine/src/flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/paragraph.dart Outdated
Comment thread engine/src/flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/paragraph.dart Outdated
Comment thread engine/src/flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/path.dart Outdated
Comment thread engine/src/flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/path.dart Outdated
Comment thread engine/src/flutter/lib/web_ui/lib/src/engine/text/line_breaker.dart Outdated
Comment thread engine/src/flutter/lib/web_ui/lib/src/engine/text/line_breaker.dart Outdated
@flutter-dashboard flutter-dashboard Bot added the CICD Run CI/CD label May 22, 2026
@kevmoo
Copy link
Copy Markdown
Contributor Author

kevmoo commented May 22, 2026

/gemini review

Copy link
Copy Markdown
Contributor

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

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 optimizes buffer handling and lookup operations within the web engine. Specifically, it replaces manual list generation with asTypedList for memory efficiency and converts Set-based lookups to switch expressions to avoid primitive boxing in dart2wasm. Feedback indicates that the removal of the codeUnitList variable in paragraph.dart may cause a compilation error in subsequent logic, and a suggestion was made to restore the variable using the optimized approach with explicit type casting.

Comment thread engine/src/flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/paragraph.dart Outdated
@kevmoo kevmoo requested a review from eyebrowsoffire May 23, 2026 05:07
@stuartmorgan-g
Copy link
Copy Markdown
Contributor

test-exempt: code refactor with no semantic change

@github-actions github-actions Bot removed the CICD Run CI/CD label May 24, 2026
@flutter-dashboard flutter-dashboard Bot added the CICD Run CI/CD label May 24, 2026
Comment thread engine/src/flutter/lib/web_ui/lib/src/engine/text/line_breaker.dart Outdated
@yjbanov
Copy link
Copy Markdown
Contributor

yjbanov commented May 26, 2026

Do the "Comparative Telemetry Matrix" numbers come from any of our existing benchmarks? If not, can we add one?

eyebrowsoffire
eyebrowsoffire previously approved these changes May 26, 2026
Copy link
Copy Markdown
Contributor

@eyebrowsoffire eyebrowsoffire left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think overall, this change seems fine to me, but I noticed the following:

In the PR description, everything described in section 1 seems inaccurate or outdated:

Direct Zero-Allocation, Zero-Copy Memory Views:

Replaced generic closures and List.generate copy loops inside paragraph.dart and path.dart with the built-in asTypedList() view from dart:ffi.
Because the resulting typed list views are consumed strictly synchronously (by utf8.decode and FallbackFontService.addMissingCodePoints) before the stack scope is restored, utilizing direct views is 100% safe and achieves the absolute theoretical ceiling of 0 heap allocations and 0 byte copies.
For the Pointer buffer inside path.dart's toSvgString, the pointer is cast to unsigned bytes first via .cast().asTypedList(length). This reinterprets signed bytes to positive unsigned bytes (
0
to
255
) entirely at the compiler level with 0 runtime cost, ensuring complete safety under utf8.decode without any custom translation loops.

This is not using the "built-in asTypedList() view from dart:ffi". It is still doing a copy, it's just copying to a Uint8List instead of a List<int>. The other two comments talking about "utilizing direct views" and using cast<Uint8> are just not accurate, this change does neither of those things.

In addition, I think the performance metrics that are reported here in the "Comparative Telemetry Matrix" aren't particularly convincing to me. In situations where there is a very marginal improvement that could be due to noise, it celebrates it as a win, and then in the case where it shows a marginal regression it just dismisses it as "comparable". Both situations are probably just noise but it's hard to say off of a single benchmark run.

That being said, the analysis of the output wasm bytecode is pretty convincing that it's better to use typed lists instead of List<int> for this purpose, so this is probably a good change overall. We might just want to change the PR description (and therefore the eventual commit message) to be a bit more accurate before merging.

Comment thread engine/src/flutter/lib/web_ui/lib/src/engine/text/line_breaker.dart Outdated
@kevmoo
Copy link
Copy Markdown
Contributor Author

kevmoo commented May 26, 2026

Do the "Comparative Telemetry Matrix" numbers come from any of our existing benchmarks? If not, can we add one?

we have a couple of existing test suites and benchmarks that track this pipeline:

  1. dev/benchmarks/microbenchmarks/lib/layout/text_intrinsic_bench.dart (which measures iteration durations in microseconds specifically testing 'Text intrinsic height').
  2. engine/src/flutter/lib/web_ui/test/ui/paragraph_performance_test.dart (which profiles frame/phase runtimes for small, medium, and large paragraphs).

The dynamic telemetry matrix reported earlier was captured during long paragraph iterations in the browser. However, as @eyebrowsoffire notes, benchmark intervals can be subject to baseline noise. The absolute proof for this optimization lies in the WebAssembly Text (WAT) bytecode diff, which confirms the complete elimination of heap allocation instructions (struct.new $BoxedInt) from the compiler output inside the layout loops.

@kevmoo kevmoo added the autosubmit Merge PR when tree becomes green via auto submit App label May 26, 2026
@auto-submit auto-submit Bot removed the autosubmit Merge PR when tree becomes green via auto submit App label May 27, 2026
@auto-submit
Copy link
Copy Markdown
Contributor

auto-submit Bot commented May 27, 2026

autosubmit label was removed for flutter/flutter/186978, because - The status or check suite Mac tool_integration_tests_4_5 has failed. Please fix the issues identified (or deflake) before re-applying this label.

@kevmoo kevmoo requested review from eyebrowsoffire and yjbanov May 27, 2026 01:55
Copy link
Copy Markdown
Contributor

@yjbanov yjbanov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM if LGT @eyebrowsoffire

@github-actions github-actions Bot removed the CICD Run CI/CD label May 27, 2026
@kevmoo kevmoo added autosubmit Merge PR when tree becomes green via auto submit App CICD Run CI/CD labels May 27, 2026
Copy link
Copy Markdown
Contributor

@eyebrowsoffire eyebrowsoffire left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

@auto-submit auto-submit Bot removed the autosubmit Merge PR when tree becomes green via auto submit App label May 27, 2026
@auto-submit
Copy link
Copy Markdown
Contributor

auto-submit Bot commented May 27, 2026

autosubmit label was removed for flutter/flutter/186978, because - The status or check suite Mac mac_unopt has failed. Please fix the issues identified (or deflake) before re-applying this label.

@kevmoo kevmoo added the autosubmit Merge PR when tree becomes green via auto submit App label May 28, 2026
@github-actions github-actions Bot removed the CICD Run CI/CD label May 28, 2026
@auto-submit
Copy link
Copy Markdown
Contributor

auto-submit Bot commented May 29, 2026

autosubmit label was removed for flutter/flutter/186978, because - The status or check suite Google testing has failed. Please fix the issues identified (or deflake) before re-applying this label.

@auto-submit auto-submit Bot removed the autosubmit Merge PR when tree becomes green via auto submit App label May 29, 2026
@flutter-dashboard flutter-dashboard Bot added the CICD Run CI/CD label May 29, 2026
@kevmoo kevmoo added the autosubmit Merge PR when tree becomes green via auto submit App label May 29, 2026
@auto-submit auto-submit Bot added this pull request to the merge queue May 29, 2026
Merged via the queue into flutter:master with commit eb9b7d0 May 29, 2026
204 checks passed
@flutter-dashboard flutter-dashboard Bot removed the autosubmit Merge PR when tree becomes green via auto submit App label May 29, 2026
@kevmoo kevmoo deleted the i186972_smarter_wasm_gc branch May 29, 2026 04:14
auto-submit Bot pushed a commit to flutter/packages that referenced this pull request May 29, 2026
flutter/flutter@e70534d...b05a9d7

2026-05-29 engine-flutter-autoroll@skia.org Roll Skia from 47155534833e to d9d6b440c4e7 (1 revision) (flutter/flutter#187301)
2026-05-29 engine-flutter-autoroll@skia.org Roll Skia from f93ed13d77fb to 47155534833e (4 revisions) (flutter/flutter#187291)
2026-05-29 kevmoo@users.noreply.github.com [web_ui] Optimize skwasm text layout and path decoding to eliminate dynamic boxing churn under Wasm (flutter/flutter#186978)
2026-05-29 engine-flutter-autoroll@skia.org Roll Fuchsia Linux SDK from SBpmmPxqx3lAvGojE... to jMR_VXQi07kAk8vbR... (flutter/flutter#187279)
2026-05-29 burak.karahan@mail.ru Remove Material import from sliver tree rendering test (flutter/flutter#187000)
2026-05-29 bdero@google.com [Impeller] Remove the y_coord_scale Y-flip plumbing (flutter/flutter#187224)
2026-05-29 31859944+LongCatIsLooong@users.noreply.github.com Add/remove overlay child RenderObject from the tree in `attach`/`detach` (flutter/flutter#186564)
2026-05-29 engine-flutter-autoroll@skia.org Roll Skia from fcfe5975c945 to f93ed13d77fb (4 revisions) (flutter/flutter#187273)
2026-05-28 evanwall@buffalo.edu Handle complex RSE rendering in the uber SDF pipeline (flutter/flutter#186434)
2026-05-28 engine-flutter-autoroll@skia.org Roll Dart SDK from 082191101fcc to 683322426411 (2 revisions) (flutter/flutter#187270)
2026-05-28 mvincentong@gmail.com Clarify route transition animations (flutter/flutter#186552)
2026-05-28 116356835+AbdeMohlbi@users.noreply.github.com document that the default Key is null and explain proper usage in list diffing (flutter/flutter#185197)
2026-05-28 srawlins@google.com [flutter_tools] Use super parameters in missed spots (flutter/flutter#186197)
2026-05-28 mr_nadeem_iqbal@yahoo.com docs: Document MediaQueryData.alwaysUse24HourFormat on macOS, Windows, Linux, web (#160664) (flutter/flutter#186642)
2026-05-28 engine-flutter-autoroll@skia.org Roll Skia from 5493e4c144cd to fcfe5975c945 (3 revisions) (flutter/flutter#187256)
2026-05-28 30870216+gaaclarke@users.noreply.github.com Shares opengles golden context (flutter/flutter#187243)
2026-05-28 jason-simmons@users.noreply.github.com Update the Curl CIPD package in .ci.yaml to version 8.20.0 (flutter/flutter#187133)
2026-05-28 737941+loic-sharma@users.noreply.github.com Improve SizedBox's docs (flutter/flutter#187208)
2026-05-28 bdero@google.com [Impeller] Support instanced rendering across all backends (flutter/flutter#186653)
2026-05-28 43054281+camsim99@users.noreply.github.com [Android] Reset system UI visibility flags when setting edge-to-edge mode (flutter/flutter#187207)
2026-05-28 engine-flutter-autoroll@skia.org Roll Skia from a38708fb7926 to 5493e4c144cd (7 revisions) (flutter/flutter#187241)
2026-05-28 30870216+gaaclarke@users.noreply.github.com Turned on impeller by default on macos (flutter/flutter#186546)
2026-05-28 mvincentong@gmail.com Clarify lazy scroll extent docs (flutter/flutter#186864)
2026-05-28 mdebbar@google.com [web] Fix WebParagraph locales test (flutter/flutter#186813)
2026-05-28 engine-flutter-autoroll@skia.org Roll Packages from 4b424d7 to 10cbdc5 (3 revisions) (flutter/flutter#187238)
2026-05-28 engine-flutter-autoroll@skia.org Roll Dart SDK from f3db7b7d9801 to 082191101fcc (8 revisions) (flutter/flutter#187235)
2026-05-28 engine-flutter-autoroll@skia.org Roll Skia from 32acea791248 to a38708fb7926 (1 revision) (flutter/flutter#187221)
2026-05-28 bdero@google.com [Flutter GPU] Add r32Float and remove Apple-only XR pixel formats (flutter/flutter#187069)

If this roll has caused a breakage, revert this CL and stop the roller
using the controls here:
https://autoroll.skia.org/r/flutter-packages
Please CC boetger@google.com,stuartmorgan@google.com 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

Documentation for the AutoRoller is here:
https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md
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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[web][dart2wasm] High-frequency integer boxing & GC churn in Skwasm text layout (paragraph.dart & line_breaker.dart)

4 participants