Repository navigation
Description
Steps to reproduce
flutter create bug- Add this to
android/app/src/main/AndroidManifest.xml, inside<application>:
<meta-data android:name="io.flutter.embedding.android.ImpellerBackend"
android:value="opengles"/>flutter build apk --debug, install, run. The engine logs:
Using the Impeller rendering backend (OpenGLES).flutter build apk --release, install, run the same source.
Nothing has to be tapped: the engine prints the backend it picked.
Expected results
The release build uses the OpenGLES backend, as the debug build does.
Actual results
The release build uses Vulkan. Same device, same manifest, same source:
debug: I flutter : [IMPORTANT:flutter/shell/platform/android/android_context_gl_impeller.cc(104)] Using the Impeller rendering backend (OpenGLES).
release: I flutter : [IMPORTANT:flutter/shell/platform/android/android_context_vk_impeller.cc(62)] Using the Impeller rendering backend (Vulkan).
Passing the switch through the embedder instead of the manifest behaves the
same way. Overriding FlutterActivity.getFlutterShellArgs() and adding
--impeller-backend=opengles is honoured in debug and ignored in release,
even though the argument demonstrably reaches the engine - this is from a
release build, logging what the override returns:
I MyApp : shell args: --impeller-backend=opengles
I flutter : [IMPORTANT:...android_context_vk_impeller.cc(62)] Using the Impeller rendering backend (Vulkan).
The Java side does not appear to drop it: in FlutterEngineFlags,
IMPELLER_BACKEND is constructed with allowedInRelease = true (verified by
disassembling flutter.jar from the 3.47.2 artifacts).
docs.flutter.dev/perf/impeller does say backend selection "will only work in
debug and profile runtime modes", so this may be intended. If so, please
consider allowing it in release. On a Mali-G57 device, reading back a
3840x2160 offscreen picture (Picture.toImage() then toByteData()) takes
5682-6055 ms on Vulkan and 48-163 ms on OpenGLES - the same work, 40-120x
apart. On an Adreno 740 the two backends are indistinguishable (25-56 ms vs
23-58 ms), so choosing OpenGLES costs nothing there. Today the only
release-time escape is EnableImpeller=false, which is deprecated, so the
workaround for that is scheduled to disappear.
Code sample
Code sample
// Minimal reproduction: the ImpellerBackend manifest entry (and the same
// switch passed through getFlutterShellArgs) is honoured in debug but ignored
// in release.
//
// The app's content is irrelevant - the engine itself logs which backend it
// picked, so nothing has to be tapped. Build this in debug and then in
// release with the same AndroidManifest.xml entry, and compare the log line.
import 'package:flutter/material.dart';
void main() => runApp(
const MaterialApp(
home: Scaffold(
body: Center(child: Text('Impeller backend probe')),
),
),
);Screenshots or Video
Screenshots / Video demonstration
Not applicable - the symptom is a log line.
Logs
Logs
See "Actual results" above.
Flutter Doctor output
Doctor output
Note on the channel:
flutter doctorreports[user-branch], but this
checkout is exactly the3.47.2tag - revision
d3b14c876900e553bc736ca19295fc09e3853e8ematchesrefs/tags/3.47.2.
It is stable 3.47.2, not a custom branch.
[!] Flutter (Channel [user-branch], 3.47.2, on Microsoft Windows [Version 10.0.26200.9168], locale ja-JP) [971ms]
! Flutter version 3.47.2 on channel [user-branch] at C:\Users\<user>\develop\flutter
Currently on an unknown channel. Run `flutter channel` to switch to an official channel.
If that doesn't fix the issue, try deleting the 'bin/cache/flutter.version.json' file in your Flutter SDK directory and then reinstall Flutter by following instructions at https://flutter.dev/setup.
• Upstream repository https://github.com/flutter/flutter.git
• Framework revision d3b14c8769 (13 days ago), 2026-08-26 16:07:51 -0700
• Engine revision a804b26164
• Dart version 3.13.2
• DevTools version 2.60.0
• Feature flags: enable-web, enable-linux-desktop, enable-macos-desktop, enable-windows-desktop, enable-android, enable-ios, cli-animations, enable-native-assets, enable-record-use, enable-swift-package-manager, omit-legacy-version-file, enable-lldb-debugging, enable-uiscene-migration, enable-riscv64
• If those were intentional, you can disregard the above warnings; however it is recommended to use "git" directly to perform update checks and upgrades.
[✓] Windows Version (11 Pro 64-bit, 25H2, 2009) [3.9s]
[!] Android toolchain - develop for Android devices (Android SDK version 37.0.0) [1,845ms]
• Android SDK at C:\Users\<user>\AppData\Local\Android\Sdk
• Emulator version unknown
• Platform android-37.0, build-tools 37.0.0
• ANDROID_HOME = C:\Users\<user>\AppData\Local\Android\Sdk
• Java binary at: C:\Users\<user>\AppData\Local\Programs\Android Studio\jbr\bin\java
This is the JDK bundled with the latest Android Studio installation on this machine.
To manually set the JDK path, use: `flutter config --jdk-dir="path/to/jdk"`.
• Java version OpenJDK Runtime Environment (build 25.0.3+-15898627-b508.16)
✗ Android license status unknown.
Run `flutter doctor --android-licenses` to accept the SDK licenses.
See https://flutter.dev/to/windows-android-setup for more details.
[✓] Chrome - develop for the web [956ms]
• Chrome at C:\Program Files\Google\Chrome\Application\chrome.exe
[✓] Visual Studio - develop Windows apps (Visual Studio Community 2026 18.9.3) [952ms]
• Visual Studio at C:\Program Files\Microsoft Visual Studio\18\Community
• Visual Studio Community 2026 version 18.9.12128.139
• Windows 10 SDK version 10.0.26100.0
[✓] Connected device (5 available) [2.8s]
• SC 51D (mobile) • RFCW31LYZJY • android-arm64 • Android 16 (API 36)
• SM A253C (mobile) • RFGL63FS5GF • android-arm64 • Android 16 (API 36)
• Windows (desktop) • windows • windows-x64 • Microsoft Windows [Version 10.0.26200.9168]
• Chrome (web) • chrome • web-javascript • Google Chrome 152.0.7977.76
• Edge (web) • edge • web-javascript • Microsoft Edge 152.0.4191.66
! Device emulator-5562 is offline.
[✓] Network resources [1,562ms]
• All expected network resources are available.
! Doctor found issues in 2 categories.
Activity
saurabh-mirajkar commentedon Sep 9, 2026
@midorinokaze Thanks for report. I was able to reproduce this on both latest stable and current master on a Pixel 6(Android 17). Labelling for further investigation.
stable
master
gaaclarke commentedon Sep 14, 2026
This is working as intended. If this is a problem with a vulkan driver we should implement a workaround or block that device from using vulkan. Can you provide the information on the device you are having a problem with?
adb shell cat /system/build.propand/or
I think those properties are what we'd need to block those devices.
midorinokaze commentedon Sep 15, 2026
Thanks - understood that the release-mode restriction is intended.
The device where Vulkan is a problem is a Samsung Galaxy A25 5G (SM-A253C),
Mali-G57 MC2, Android 16:
/system/build.propis not readable on this device (Permission denied,stock Samsung firmware); the values above are from
getprop. Happy to pastethe full
getpropdump if it helps.What goes wrong on it: reading back a large offscreen picture
(
Picture.toImage(3840, 2160)followed bytoByteData(rawRgba)) takes5682-6055 ms per image with Impeller-Vulkan and 48-163 ms with
Impeller-OpenGLES, same build, same scene (debug builds, switched with the
ImpellerBackendmanifest entry). Drawing the picture itself is 91-135 ms onboth. The readback runs on the platform thread, so the app produces no frames
for ~6 s per image; during that window the compositor shows a stale frame
from a previously removed route (this looks like #189767), and
Choreographerreports
Skipped 227 frames!for com.android.systemui as well.For comparison, on two other devices the backends behave the same:
back to OpenGLES; 127-168 ms.
One caveat on reproducing it: the ratio depends on what is drawn. The scene
above is a few thousand stroked paths (Natural Earth coastlines and borders,
~82,000 points). With synthetic scenes of a few hundred stroked polylines the
two backends are within 2x of each other on the same Mali device. If a
standalone reproduction would help, I can put one together with the real
data (it is public domain), but it would need a small repo rather than an
inline sample.
midorinokaze commentedon Sep 15, 2026
Follow-up with two more devices, all three measured with the same harness
(debug builds of the same app, one APK per backend, switched only through the
ImpellerBackendmanifest entry; devices run one at a time). The work is:draw the map picture,
Picture.toImage(3840, 2160), thentoByteData(rawRgba). Times are toImage + first readback of one 3840x2160image, two runs each:
universal7884B) / Mali-G71 MP2 / v1.r26p0So the Mali-G71 device is worse than the Mali-G57 one: 16 s per image, about
12x OpenGLES on the same device. The A21 number matches what the release
build of the app did before I opted out of Impeller (17.1 s per 3840x2160
band), so the debug harness is not distorting it.
On the A21 a larger picture (7680x4320, the harness's second case) does not
just take longer on Vulkan - the device is lost, and from then on every draw
and readback in the process fails, including the 3840x2160 ones that had
worked a moment earlier:
OpenGLES handles the same 7680x4320 picture on that device in 1.6-1.9 s plus
a 1.2-2.4 s readback. The A25 renders it on Vulkan in 26.8 s (OpenGLES:
0.8-1.5 s plus 0.6-1.5 s).
Identifiers for the A21:
The two Mali devices are different GPU generations (G71 / G57), different SoC
vendors (Samsung Exynos / MediaTek) and different driver branches (r26p0 /
r38p1), and neither Adreno device shows a gap between the backends, so this
looks Mali-wide rather than specific to one SoC.
Side note on the Adreno 508 (Sony 902SO, sdm660, Android 10): with no
ImpellerBackendentry the engine fails to initialise Vulkan and falls backto OpenGLES, which works. With
ImpellerBackend=vulkanset explicitly itaborts at startup instead of falling back:
Probably expected for an explicit request; mentioning it only for completeness.
gaaclarke commentedon Sep 15, 2026
Based on what you are saying, it sounds like it may be a vulkan bug. It's at least evaluating it as such. Can you please provide a repro and we can take a look?
FWIW Android 10 will be using the Skia backend not Impeller. That means the calculus for determining work to fix bugs for it has to be weighed against the fact that it is on the chopping block.
midorinokaze commentedon Sep 15, 2026
Here is a standalone reproduction:
https://github.com/midorinokaze/impeller_mali_readback_repro
It is a
flutter createapp with one asset: the already projected pixelcoordinates and paint parameters of the map scene (Natural Earth, public
domain; 76 draw calls, ~83,000 points, 652 KB).
main.dartreplays theminto a
PictureRecorder-drawRect,drawPath(even-odd fill + round-joinstroke),
drawCircle- then doestoImage(3840, 2160)+toByteData(rawRgba)three times, once at 7680x4320, and once more at 3840x2160, printing the
timings to the log. Nothing has to be tapped. The
ImpellerBackendmeta-datafor switching to OpenGLES is in the manifest, commented out.
Same numbers as the app (Flutter 3.47.2 stable, debug builds):
ErrorDeviceLost, every later draw failsOn the two Mali devices the Vulkan cost is in the first
toByteData(that iswhere the picture is actually rasterised) and is 25-65x the OpenGLES time on
the same device; on the Adreno 740 the two backends are within 2x of each
other. The A21 log for the 7680x4320 case is in the README.
[Android] Log why Impeller falls back to OpenGLES (flutter#194764)
[Android] Log why Impeller falls back to OpenGLES (flutter#194764)