Skip to content

[Impeller] Impeller is significantly slower than Skia on Adreno 702 (QCM2290) — 3.5x worse raster, Flutter 3.47.2 #192364

Open

Description

@beecomm-noam

Steps to reproduce

  1. Build a release APK of a portrait tablet app with two screens: a scrollable
    2-column grid of Material cards, each card holding a name and a price label,
    next to a side column of category buttons; and a home screen that draws about
    fifteen rounded shapes with strokes, plus text and small badges.
  2. Install on a wizarPOS Q3 (Qualcomm QCM2290, Adreno 702, Android 14 / API 34,
    arm64-v8a).
  3. Measure with Impeller enabled (the default).
  4. Add the opt-out to AndroidManifest.xml and measure again on the same build:
<meta-data
    android:name="io.flutter.embedding.android.EnableImpeller"
    android:value="false" />
  1. Compare cold start (adb shell am start -W) and dropped frames.

Expected results

Impeller performs at least as well as Skia, so that removing the opt-out (which is
now deprecated) is safe.

Actual results

Impeller is worse on every measure on this device. It is worst in raster, which is
the part Impeller owns.

All numbers are release builds, same commit, same device, same 597 KB menu,
three runs each. Frame data comes from an in-app FrameTiming watchdog that reports
one summary per burst of janky frames: how many vsyncs were lost, the worst build
and raster times, and which thread was late.

Cold start — adb shell am start -W, TotalTime

Run Skia Impeller (Vulkan)
1 1174 ms 1376 ms
2 1188 ms 1379 ms
3 1160 ms 1362 ms
median 1174 ms 1376 ms (+17%)

Home screen, first ~2.5 s after launch — lost vsyncs per launch

Run Skia Impeller (Vulkan)
1 39 63
2 46 67
3 44 84
median 44 67

Worst raster time in that same window is the striking part:

Run Skia Impeller (Vulkan)
1 47.7 ms 234.7 ms
2 59.3 ms 174.0 ms
3 56.7 ms 202.3 ms

Under Skia the worst burst is blamed on the UI thread (our own build work). Under
Impeller the same burst becomes raster-bound, with a single raster pass of up to
234 ms — roughly 14 dropped frames from one frame's raster.

Representative raw records, Skia then Impeller, same moment in the same screen:

{"screen":"/","first":false,"frames":17,"lost":39,"worstBuildMs":81.4,"worstRasterMs":47.7,"worstTotalMs":127.3,"blame":"ui"}
{"screen":"/","first":false,"frames":11,"lost":63,"worstBuildMs":66.2,"worstRasterMs":234.7,"worstTotalMs":271.4,"blame":"raster"}

Entering the card grid, then 16 flings — total lost vsyncs

Run Skia Impeller (Vulkan)
1 30 49
2 17 50
3 17 44
median 17 49

Scrolling is the clearest difference. Under Skia the 16 flings produce essentially
no jank at all (0–1 lost vsyncs across all three runs; every lost frame is on entry).
Under Impeller each run produces two or three additional raster-blamed bursts
during the flings:

{"screen":"/categories","first":false,"frames":10,"lost":15,"worstBuildMs":2.3,"worstRasterMs":42.9,"blame":"raster"}
{"screen":"/categories","first":false,"frames":7,"lost":9,"worstBuildMs":1.5,"worstRasterMs":41.4,"blame":"raster"}

Build time is ~2 ms in those bursts, so this is not the app doing work — it is
raster alone missing the 16.7 ms budget while scrolling a plain card grid.

Code sample

I do not have a minimal reproduction yet — this is a proprietary point-of-sale app.
The affected screens use only ordinary Material widgets: GridView/ListView of
Card with Text, rounded borders, and a custom-painted home screen of rounded
rectangles with strokes and text. No shaders, no BackdropFilter, no platform
views, and no images in the scrolling grid.

I am happy to build and attach a minimal reproduction, or capture a Perfetto trace
from a profile build on this device, if that would help. Please say which is more
useful and I will follow up.

Screenshots or Video

Rendering is visually correct under Impeller — the two renderers produce the same
screen. This is purely a performance report, not a correctness one.

Logs

Startup message that prompted this report:

I flutter : [IMPORTANT:flutter/shell/common/shell.cc(528)] [Action Required]: Impeller opt-out deprecated.
I flutter :     The application opted out of Impeller by either using the
I flutter :     `--no-enable-impeller` flag or the
I flutter :     `io.flutter.embedding.android.EnableImpeller` `AndroidManifest.xml` entry.
I flutter :     These options are going to go away in an upcoming Flutter release. Remove
I flutter :     the explicit opt-out. If you need to opt-out, please report a bug describing
I flutter :     the issue.

Confirming Impeller was genuinely active for the "Impeller" column:

I AdrenoVK-0: Driver Path : /vendor/lib64/hw/vulkan.adreno.so
I flutter : [IMPORTANT:flutter/shell/platform/android/android_context_vk_impeller.cc(62)] Using the Impeller rendering backend (Vulkan).

Device:

ro.product.model        = WIZARPOS_Q3
ro.product.manufacturer = wizarPOS
ro.board.platform       = bengal
ro.soc.model            = QCM2290
ro.build.version.release= 14
ro.build.version.sdk    = 34
ro.product.cpu.abi      = arm64-v8a
GPU                     = Adreno 702
Vulkan driver           = vulkan.adreno.so

This is an entry-level device, but it is the hardware a lot of point-of-sale
terminals ship with, and it is in daily use in restaurants. We cannot move to
Impeller while scrolling costs three times as many dropped frames as Skia, so the
removal of EnableImpeller would be a real regression for us.

Related issues

Flutter Doctor output

[√] Flutter (Channel stable, 3.47.2, on Microsoft Windows [Version 10.0.26200.9278], locale he-IL)
    • Flutter version 3.47.2 on channel stable at C:\src\flutter
    • Upstream repository https://github.com/flutter/flutter.git
    • Framework revision d3b14c8769 (11 days ago), 2026-08-26 16:07:51 -0700
    • Engine revision a804b26164
    • Dart version 3.13.2
    • DevTools version 2.60.0

[√] Windows Version (Windows 11 or higher, 25H2, 2009)

[√] Android toolchain - develop for Android devices (Android SDK version 36.0.0)
    • Platform android-36, build-tools 36.0.0
    • Java version OpenJDK Runtime Environment (build 21.0.8+-14196175-b1038.72)
    • All Android licenses accepted.

[√] Connected device (4 available)
    • WIZARPOS Q3 (mobile) • WP6201DQ36000247 • android-arm64 • Android 14 (API 34)

Activity

  1. added
    platform-androidAndroid applications specifically
    engineflutter/engine related. See also e: labels.
    c: performanceRelates to speed or footprint issues (see "perf:" labels)
    in triagePresently being triaged by the triage team
    e: impellerImpeller rendering backend issues and features requests
    and removed
    in triagePresently being triaged by the triage team
    on Sep 7, 2026
  2. mbcorona commented on Sep 7, 2026

    @mbcorona
    Member

    HI @beecomm-noam , thanks for the detailed report. Routing to the engine team for further investigation.

  3. gaaclarke commented on Sep 14, 2026

    @gaaclarke
    Member

    @beecomm-noam can you try first to force opengles on that device. There are instructions here: https://github.com/flutter/flutter/blob/master/engine/src/flutter/impeller/README.md#android

    Some devices have poorly implemented vulkan drivers and that's the first thing we should check since our automated performance tests show Impeller should outperform Skia in the general case.

    Have you attempted your reproduction with another device and got different results?

  4. added
    P2Important issues not at the top of the work list
    on Sep 14, 2026
  5. beecomm-noam commented on Sep 15, 2026

    @beecomm-noam
    Author

    Thanks @gaaclarke. I have an answer to both of your questions, and I think they
    point at the Vulkan backend rather than at Impeller.

    1. ImpellerBackend cannot be set in the mode that regresses.

    The README says the meta-data only works in debug and profile, and that is
    what I see. A release build with

    <meta-data android:name="io.flutter.embedding.android.ImpellerBackend"
               android:value="opengles" />

    still logs android_context_vk_impeller.cc(62)] Using the Impeller rendering backend (Vulkan). A profile build of the same code honours it and logs
    android_context_gl_impeller.cc(104)] ... (OpenGLES).

    But profile mode does not reproduce the bug at all. All three configurations
    land in the same range, and every janky burst is blamed on the UI thread rather
    than on raster. Medians of the launch burst on the home screen, 3 runs each:

    profile build lost vsyncs worst raster blame
    Skia 33 35.2 ms ui
    Impeller Vulkan 36 41.5 ms ui
    Impeller OpenGLES 40 46.8 ms ui

    So on the Q3 I cannot answer the Vulkan question with the flag as it stands. If
    there is a way to force the GL backend in a release build, I will run it.

    2. A second device, and it does not reproduce — because Impeller uses GL there.

    I ran the same app on a Kozen L200 payment terminal: MediaTek MT6761 with a
    PowerVR GPU, Android 10 / SDK 29, 32-bit armeabi-v7a, 720x1280 @ 60 Hz. Different
    manufacturer, different SoC vendor, different GPU family from the Q3.

    On this device Impeller picks the GL backend by itself, in a release build —
    no override, the Vulkan driver is simply not usable:

    [IMPORTANT:flutter/shell/platform/android/android_context_gl_impeller.cc(104)]
    Using the Impeller rendering backend (OpenGLES).
    

    Release, medians of 3 runs each:

    Kozen L200 Skia Impeller (OpenGLES)
    cold start am start -W TotalTime 773 ms 819 ms (+6%)
    launch burst, lost vsyncs 14 13
    worst raster in that burst 33.3 ms 25.3 ms

    Impeller is better at raster than Skia on this hardware and equal on dropped
    frames, at a ~6% cold start cost. There is no raster blowup anywhere.

    3. Re-measured the Q3 on today's build too.

    wizarPOS Q3 (Adreno 702) Skia Impeller (Vulkan)
    cold start TotalTime 1208 ms 1223 ms
    launch burst, lost vsyncs 24 25
    worst single raster across 3 runs 72.6 ms 150.1 ms

    The app has changed a lot since I opened this issue, so the original table should
    be read as stale. Cold start and dropped-frame counts are now effectively equal.
    What survives is the raster tail: single raster passes about twice as long as the
    worst Skia one.

    Summary: Impeller on the GL backend is fine on low-end hardware — the L200
    shows it beating Skia. The long raster passes only appear on the Q3, which is the
    one device of the two that runs Impeller on Vulkan. That matches your suspicion
    about the driver. I am happy to run any trace or test build you want on either
    device.

  6. codewithsuperior commented on Sep 18, 2026

    @codewithsuperior

    @gaaclarke — on your question of whether this reproduces on another device, it does, and on the OpenGLES backend rather than Vulkan: #193009.

    Different vendor (PowerVR Rogue GE8320 on MT6765, armeabi-v7a, Android 14), engine selecting GLES on its own, release builds only. Same shape of result as here but more extreme: 400 of 400 frames of a scripted scroll late and raster-bound under Impeller, against 64 of 1000 under Skia, from identical Dart. Mean buildDuration was 1.5 ms on both renderers, so the difference is entirely raster.

    Since that one is GLES and this one is Vulkan, a bad Vulkan driver would not explain both — which may argue against the e: device-specific label on the pair of them.

  7. njemouipro commented on Nov 1, 2026

    @njemouipro

    I'm seeing the same pattern on a different GPU vendor: a Mali-G31 (Rockchip RK3326, Android 11, 32-bit, no Vulkan). Impeller falls back to OpenGLES, and the median raster time is about 2x Skia on the same APK. It is unchanged on beta and master.

    I used a minimal repro: flutter create, a single main.dart, no packages and no images. One tab is a ListView of 60 simple cards, the other is 40 paragraphs of plain text. I built it in profile mode for android-arm, scrolled it with identical adb swipes (3 passes × 8 swipes per tab), and read FrameTiming from the VM service.

    Flutter Tab Impeller OpenGLES raster p50 / p90 (ms) · janky Skia raster p50 / p90 (ms) · janky
    3.44.3 stable Cards 10.9 / 16.1 · 4.2 % 6.2 / 16.0 · 3.0 %
    3.44.3 stable Text 10.9 / 16.2 · 5.1 % 5.7 / 15.5 · 2.0 %
    3.50.0-0.3.pre beta Cards 11.3 / 16.0 · 5.9 % 6.0 / 8.6 · 1.6 %
    3.50.0-0.3.pre beta Text 11.2 / 14.9 · 3.3 % 5.6 / 15.6 · 2.1 %
    master 327cc39 Cards 11.1 / 16.2 · 5.9 % 5.6 / 15.7 · 2.6 %
    master 327cc39 Text 11.0 / 14.7 · 2.8 % 5.8 / 8.1 · 1.4 %

    Logcat confirms Using the Impeller rendering backend (OpenGLES) in the default launch. With --ez enable-impeller false, it shows the Impeller opt-out deprecated warning instead.

    Even a text-only page doubles in raster cost, so the gap does not come from a specific widget. In a real app on this device, card lists take 21-24 ms of raster under Impeller OpenGLES, so every frame is janky. The same APK takes 6-7 ms under Skia. The timeline is dominated by ReactorGLES::React. The #193427 fix (in 3.50 beta) does not change these numbers on this GPU.

    I'm happy to share the full repro, more traces, or test a patch on this device.

    Full report with the minimal repro and the doctor output: #195767

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Important issues not at the top of the work listc: performanceRelates to speed or footprint issues (see "perf:" labels)e: device-specificOnly manifests on certain devicese: impellerImpeller rendering backend issues and features requestsengineflutter/engine related. See also e: labels.found in release: 3.47Found to occur in 3.47platform-androidAndroid applications specificallyteam-engineOwned by Engine teamtriaged-engineTriaged by Engine team

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions