Repository navigation
Description
Steps to reproduce
Device: any MediaTek phone whose vendor partition reports ro.vendor.build.version.sdk = 31 and
whose Vulkan driver works. Two used here:
| Samsung Galaxy A13 SM-A137F | Samsung Galaxy A32 SM-A325F | |
|---|---|---|
ro.board.platform / ro.hardware |
mt6768 / mt6768 |
mt6768 |
ro.soc.manufacturer / ro.soc.model |
Mediatek / MT6769V/CT |
Mediatek / MT6768 family |
| GPU / driver | Mali-G52 MC2, OpenGL ES 3.2 v1.r32p1-01eac0 (dumpsys SurfaceFlinger), Vulkan ICD vulkan.mt6768.so, ro.hardware.egl=meow |
Mali-G52 |
Android / ro.build.version.sdk |
13 / 33 | 13 / 33 |
ro.vendor.build.version.sdk |
31 | 31 |
| Vulkan advertised | android.hardware.vulkan.version=4198400 (1.1), level=1 |
yes |
| ABI list | armeabi-v7a, armeabi (32-bit ROM, 64-bit-capable SoC) |
64-bit |
| Display / RAM | 1080×2408 / 3.7 GB | 1080×2400 / 4 GB |
flutter create repro(created with 3.44.2 sosdk: ^3.12.2fits both SDKs). Nothing else is needed for the backend line.- Build and run the same project with each SDK:
~/fvm/versions/3.44.2/bin/flutter build apk --release && adb install -r build/app/outputs/flutter-apk/app-release.apk adb logcat -c && adb shell monkey -p com.example.repro 1 && adb logcat | grep "rendering backend" ~/fvm/versions/3.47.1/bin/flutter build apk --release # then the same install / logcat - For the memory part, use the two-file variant at https://github.com/Daracul/impeller-gate-repro
(a hidden full-screenFlutterViewdrawing one full-screenClip.antiAliasWithSaveLayer) and read
adb shell dumpsys meminfo <pkg> | grep "GL mtrack"~20 s after launch.
Expected results
Devices that ran Impeller Vulkan without issues on 3.44 keep it on 3.47, or the gate targets the
configuration that actually failed — #186067 is MT8788 with Mali-G57 — rather than every MediaTek
device whose vendor partition happens to report SDK 31.
Concretely, any of these would keep these devices on Vulkan while still covering #186067:
- Key the MediaTek rule on the platform (
ro.board.platform/ the Vulkan ICD name,vulkan.mt8788.so
vsvulkan.mt6768.so) and deny the platforms where the gralloc bug reproduces, the way PowerVR
B-series is handled by denylist today. - Or gate on the Mali driver release (
r32p1here) through the existing known-bad-driver path for
Vulkan, instead of on the vendor partition's SDK number. - Keep
ro.vendor.build.version.sdkonly as a fallback when neither the platform nor the driver is
recognised.
ro.vendor.build.version.sdk is a proxy for driver vintage, and a coarse one: it cannot tell
Mali-G52 on MT6768 with a working r32p1 driver from Mali-G57 on MT8788 with the gralloc bug.
Actual results
The backend flips with the SDK, nothing else changed:
| SDK | engine | logcat |
|---|---|---|
| 3.44.2 | 77e2e94772b6eb43759e34ed1ad7da4674e19cab |
android_context_vk_impeller.cc(62) Using the Impeller rendering backend (Vulkan). |
| 3.47.1 | 5d531788691ec3404cac0cee66ead4007b177363 |
android_context_gl_impeller.cc(104) Using the Impeller rendering backend (OpenGLES). |
Deterministic on every launch; reinstalling the 3.44.2 build restores Vulkan. Nothing in the 3.47.1
logcat says why Vulkan was skipped. The literal ro.vendor.build.version.sdk is present in the
3.47.1 libflutter.so (armeabi-v7a) and absent from the 3.44.2 one.
Why: #186405 ("Bumped required mediatek vender sdk version", merged 2026-05-12, in 3.47) replaced
the API-level gate for MediaTek Vulkan with a vendor-SDK gate and raised the threshold to 32
"since the bug was reported on version 31". That bug, #186067, is a gralloc format rejection on
MT8788 / Mali-G57. Mali-G52 on MT6768/MT6769 has the same vendor SDK number but not the bug — it
ran Vulkan on 3.44 in production — and gets swept in. Samsung and many MediaTek-BSP phones rarely
rebuild the vendor partition, so on these devices ro.vendor.build.version.sdk stays at 31 through
Android 13; for them the gate is effectively permanent.
There is no app-side way around it in release: per #192467 the release-mode ImpellerBackend
restriction is intended, and the suggested route is to fix the device gating in the engine — which is
what this issue asks for.
Production impact (inDrive Android app; per-user daily median of the Graphics segment of PSS
from in-app memory telemetry (dumpsys meminfo), p50 across users, 5 days before/after the
release that moved from 3.44.2 to 3.47.1; figures are shares of the release's user base):
| Device (Android) | SoC / GPU | vendor SDK | p50 Graphics MB, 3.44.2 | p50 Graphics MB, 3.47.1 |
|---|---|---|---|---|
| Samsung A32 SM-A325F/M (13) | MT6768 / Mali-G52 | 31 | 16 | 121 / 132 |
| Samsung A22 SM-A225M (13) | MT6769 / Mali-G52 | 31 | 10 | 78 |
Redmi 13C gale (13 / 14) |
MT6769 / Mali-G52 | < 32 | 4 | 79 / 83 |
Redmi 13C gale (15) — vendor rebuilt |
MT6769 / Mali-G52 | ≥ 32 | 63 | 65 |
| control: Samsung A12 (PowerVR, GLES on both) | MT6765 / PowerVR | — | 53 | 52 |
control: Redmi 10 selene |
MT6769 / Mali-G52 | ≥ 32 | 11 | 10 |
| control: Qualcomm Adreno ≤ 650 | — | — | +2…+5 |
- 14.3 % of the release's users are in flipped model × Android cells (Δ ≥ 30 MB); user-weighted
mean Δ +76 MB; among affected users 28 % gained 30–60 MB, 53 % 60–100 MB, 19 % ≥ 100 MB. - While the app is not in the foreground: same 14.3 % share, mean +66 MB — the extra graphics
memory is not released on backgrounding. - The Redmi 13C rows are the cleanest control: same phone, the Android 15 build has a rebuilt vendor
partition (SDK ≥ 32) and shows no regression; Android 13/14 builds do. - Footprint scales with screen resolution, consistent with full-screen render targets.
On-device, production app, GL mtrack (MB), same account and tap path. A13:
| Point | Vulkan (3.44.2) | GLES (3.47.1) | Δ |
|---|---|---|---|
| Flutter engine created, no Flutter view attached yet | 4.5 | 20.4 | +15.9 |
| first Flutter screen | 6.9 | 46.0 | +39.2 |
| back on a native screen | 6.9 | 53.7 | +46.8 |
| second Flutter screen | 20.0 | 59.9 | +39.8 |
| backgrounded | 19.5 | 50.4 | +30.9 |
A32 (Graphics): cold start 22.6 → 95.8 (+71.4), Flutter screen 29.7 → 123.1 (+91.2), backgrounded
20.3 → 88.8 (+66.9). Java heap, native heap and EGL mtrack are flat; the whole delta is GL mtrack.
Stability: Crashlytics for SM-A137F over the 3.44.2-based release (Vulkan) shows no crash issues
with libflutter, Mali, Vulkan or Impeller in the title or subtitle; the same search over the
3.47.1-based release (GLES) is also empty. So on this SoC the gate removes no observed Vulkan crash —
it only removes Vulkan. (The Impeller GLES path on Mali has open crash reports of its own, e.g.
#190640 on Android 10 devices.)
Mechanism (minimal app, hidden full-screen FlutterView, GL mtrack KB):
| Dart content in the hidden view | 3.44.2 Vulkan | 3.47.1 GLES | Δ |
|---|---|---|---|
empty Material |
6640 | 5312 | −1.3 MB |
one full-screen Clip.antiAliasWithSaveLayer, persistent |
17652 | 29076 | +11.4 MB |
| same saveLayer for 1 s, then a static frame | 17556 | 28564 | +11.0 MB |
| same 1 s saveLayer, then ~300 layer-free frames | 6788 | 10636 | +3.8 MB |
An empty frame is free on both backends. One offscreen pass allocates a full-screen render target,
and the numbers are consistent with its MSAA attachment being lazily allocated on Vulkan (tiler)
but fully resident on GLES. The target is held until later frames let the render-target cache evict
it (see #190613); after ~300 frames GLES was still ~5 MB above its own baseline. Destroying the
engine returns everything on both backends. This is the known cost
of the GLES backend — the point here is that the gate now imposes it on devices where Vulkan worked.
Code sample
Stock flutter create output reproduces the backend flip. The two-file variant behind the memory
table (a plain Activity attaching a covered full-screen FlutterView(FlutterTextureView), and a
main.dart with one full-screen saveLayer) is at https://github.com/Daracul/impeller-gate-repro.
lib/main.dart (core)
class ReproApp extends StatelessWidget {
const ReproApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(
debugShowCheckedModeBanner: false,
home: ClipRRect(
clipBehavior: Clip.antiAliasWithSaveLayer, // one offscreen pass per frame
borderRadius: BorderRadius.all(Radius.circular(1)),
child: Material(
color: Colors.white,
child: Stack(children: [
Positioned.fill(child: ColoredBox(color: Colors.indigo)),
Positioned(left: 40, top: 40, right: 40, bottom: 40,
child: ColoredBox(color: Colors.orange)),
]),
),
),
);
}
}Screenshots or Video
Not applicable — the result is the backend line in logcat and dumpsys meminfo numbers.
Logs
# 3.44.2
I/flutter: [IMPORTANT:flutter/shell/platform/android/android_context_vk_impeller.cc(62)] Using the Impeller rendering backend (Vulkan).
# 3.47.1
I/flutter: [IMPORTANT:flutter/shell/platform/android/android_context_gl_impeller.cc(104)] Using the Impeller rendering backend (OpenGLES).
GL mtrack sampled once per second from process start of the production app on SM-A137F.
3.47.1/GLES: 6.0 MB at 3.2 s → 17.7 MB at 4.8 s → peak 22.3 MB → flat 18.9 MB; the step lands
~1 s after the backend line, before any Flutter view is attached. 3.44.2/Vulkan: flat 4.5 MB.
Related: #186405 (the gate change), #186067 (MT8788 / Mali-G57 bug it fixed), #192467
(release-mode ImpellerBackend is intended to be ignored), #193009 (Impeller GLES on MediaTek
MT6765 in 3.47.1), #190640 (GLES ImageDecoderImpeller SIGABRT on Android 10 Mali devices), #189190 (GLES crash on
MediaTek Android 13), #190613 (render-target cache / GPU memory on EGL), #190618 / #175577 (GLES
cold start slower), #192798 (MT8788 Vulkan platform-view corruption — the chip the gate was meant for).
Flutter Doctor output
[!] Flutter (Channel stable, 3.47.1, on macOS 14.8.2 23J126 darwin-arm64) [2.8s]
• Flutter version 3.47.1 on channel stable at ~/fvm/versions/3.47.1
• Upstream repository https://github.com/flutter/flutter.git
• Framework revision 6655482ec0 (5 weeks ago), 2026-08-19 10:07:23 -0700
• Engine revision 5d53178869
• Dart version 3.13.1
• 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
[!] Android toolchain - develop for Android devices (Android SDK version 36.0.0) [4.4s]
• Android SDK at ~/Library/Android/sdk
Activity
mbcorona commentedon Sep 22, 2026
Hi @Daracul, thanks for the excellent reporting.
Routing to team-engine for further investigation
gaaclarke commentedon Oct 14, 2026
Thanks for the report.
#186067 wasn't a crash, it was a visual glitch. One app running successfully with vulkan doesn't mean all vulkan apps will render successfully. We have to choose based off of all the things a user could do (platform views, external textures, etc.).
We're open to broadening the gate if you can provide testing for the A13 and A32 on 3.44.2. Run the following tests:
The first should show a full-screen smiley face on a yellow background. The second should show a blue/orange gradient, and it should still look right after you background and resume the app. Please include the Using the Impeller rendering backend line from logcat to confirm it ran on Vulkan, and attach screenshots.
M0n7y5 commentedon Oct 19, 2026
Second device, same symptom: Redmi Note 12 Pro 5G (MT6877, Mali-G68 MC4, Android 14, vendor SDK 31).
MT6877)v1.r32p1-01eac0_textureand_texture2)On 3.47.7, an engine patched to log each fallback branch shows the MediaTek vendor-SDK gate firing:
MediaTek vendor API level 31 is below 32 (ro.vendor.mediatek.platform=MT6877), falling back to OpenGLES.(android_context_dynamic_impeller.cc(116)).3.44.2
android_engine_test(smiley and gradient apps, profile), backend line for both apps:Default selection picks Vulkan on 3.44.2 because its MediaTek gate compares the device API level (34) against 31, not the vendor SDK.
Both apps render correctly, with 0 px diff after resume and no crash. Screenshots below are from the default build. The forced (meta-data) and default screenshots are byte-identical, so only the default ones are attached.
Screenshots (default build): smiley at 10 s, gradient at 10 s, gradient after background and resume.
Forced Vulkan on 3.47.7 renders correctly, with raster p50 of Skia 1.59 / GLES 5.35 / Vulkan 3.74 ms.
Caveat: Vulkan logs the #186067 gralloc rejection as 4 failed 4x4
R8/R10G10B10A10probes at startup and once per resume, never continuously. It causes no visible defect (pixel diff vs GLES max 1/255).Could the gate key on the Mali driver range (
r26p0in #186067 vsr32p1here) or the platform instead of the vendor SDK? As written it applies to every MediaTek device regardless of GPU.Raw artifacts (logcat, vkjson, Perfetto traces) are available on request.
This comment was prepared with AI assistance.
Daracul commentedon Oct 20, 2026
Thanks — ran both tests on two devices on Flutter 3.44.2 (engine 77e2e94), debug builds of
dev/integration_tests/android_engine_testfrom the SDK (--target-platform android-armfor the A13's 32-bit ROM,android-arm64for the A32).Devices:
ro.board.platform/ro.hardware=mt6768), Mali-G52 MC2, GLES driverOpenGL ES 3.2 v1.r32p1-01eac0, Vulkan ICDvulkan.mt6768.so, Android 13 (API 33),ro.vendor.build.version.sdk= 31.ro.board.platform=mt6768,ro.hardware=mt6769t), Mali-G52 MC2, GLES driverOpenGL ES 3.2 v1.r26p0-01eac0(the same r26p0 driver generation as the #186067 device), Vulkan ICDvulkan.mt6768.so, Android 13 (API 33),ro.vendor.build.version.sdk= 31.All four runs log:
1.
lib/external_texture/surface_producer_smiley_face_main.dart— full-screen smiley on yellow on both devices, unchanged after 14 s.A13 / A32:
2.
lib/platform_view/texture_layer_hybrid_composition_platform_view_main.dart— blue/orange gradient with the Flutter overlay on both devices; after HOME and resume the frame is pixel-identical to the launch screenshot (same PNG hash). For completeness: on both devices the test's green box under the platform view is visible for ~60–100 ms (2–4 recorded frames) before the first platform-view frame lands; a build withImpellerBackend=opengleson the A32 shows the same, so it is the texture-layer handoff, not backend-specific.A13, launch / after background + resume:
A32, launch / after background + resume:
A32 screen recording (launch → background → resume):
a32-platform-view-launch-background-resume.mp4
No
E/flutterlines in any run.One observation that may help with the gate decision: both devices log the same gralloc lines that #186067 shows at startup (
Unrecognized and/or unsupported format 0x38 and usage 0xb00,isSupported(1, 1, 56, 1, ...) failed with 5,Failed to allocate (4 x 4) ... format 56 usage b00). As far as I can tell they come from Android'slibvulkan(GetPhysicalDeviceSurfaceFormatsKHRprobesAHARDWAREBUFFER_FORMAT_R8_UNORMas a swapchain format; usage 0xb00 = COMPOSER_OVERLAY | GPU_SAMPLED_IMAGE | GPU_FRAMEBUFFER), this gralloc rejects R8 and libvulkan leaves it out of the list, while Impeller requests RGBA8 for its swapchain. On these two devices rendering is correct with those lines present, so they may not be a reliable signal of a broken Vulkan path by themselves. The defect reported in #186067 was in WebRTC video output, which these two tests do not cover — if there is a test for that path, I am happy to run it on both devices.Together with @M0n7y5's Redmi Note 12 Pro 5G above (MT6877, Mali-G68 MC4, driver r32p1) this covers three MediaTek Mali devices with vendor SDK 31 — Mali-G52 on both driver generations (r26p0 and r32p1) and Mali-G68 — all rendering both tests correctly on Vulkan. The A32 result also narrows the options from the issue description: it runs the same r26p0 driver generation as the #186067 device and renders correctly, so the driver release alone does not separate the two cases. Keying the gate on the platform (
ro.board.platform/ Vulkan ICD name,mt8788vsmt6768), or on GPU model plus driver release through the existing known-bad-driver path (Mali-G57 r26p0), would cover #186067 without sweeping in these devices.Full logcat of each run is available on request.
[Android] Log why Impeller falls back to OpenGLES (flutter#194764)
[Android] Log why Impeller falls back to OpenGLES (flutter#194764)