Repository navigation
Description
Steps to reproduce
- Build a text-heavy Flutter app in release mode and run it on a device with a PowerVR BXM GPU — I'm using a Motorola Moto G64 5G (MediaTek Dimensity 7025, PowerVR BXM-8-256).
- Display a large amount of text with many distinct glyphs. Complex scripts reach the failure fastest; I hit it with Tamil, but Latin digits are affected too.
- Scroll the list up and down several times.
The attached code sample reproduces it standalone in a few seconds of scrolling.
Note this is release-mode specific in practice, because the usual ImpellerBackend=opengles escape hatch is compiled out of release builds (filed separately).
Expected results
All glyphs render, and stay rendered.
Actual results
Glyphs are randomly dropped from rendered text. The text is laid out correctly — advance widths are preserved and nothing reflows — the glyph images simply do not appear, leaving blank space of exactly the right size. Forcing a repaint (scrolling the row off-screen and back) usually restores them.
Examples from production:
400renders as00Notrenders asN- A 10-digit phone number renders with digits missing and their spacing intact
- Tamil words lose interior letters:
திருத்து வாடிக்கையாளர்renders asருத்து வாடிக்கை ளர்
Latin digits dropping out of a bundled Roboto is what rules out a font or shaping problem: shaping clearly produced the correct glyph IDs and positions, and only rasterized glyph images went missing. Combined with the non-determinism (repaint repairs it) and the fact that the same APK renders perfectly on every other device we ship to, this points at the glyph atlas rather than anything upstream of it.
This is a production app used in Tamil, so the impact is severe — amounts and phone numbers render as plausible but wrong values rather than as visibly broken text.
Analysis: why this device is not caught by the PowerVR denylist
impeller/renderer/backend/vulkan/driver_info_vk.cc:
if (powervr_gpu_.has_value() && powervr_gpu_.value() < PowerVRGPU::kBXE) {
return true;
}with the enum in driver_info_vk.h:
enum class PowerVRGPU {
kUnknown,
// Not good.
kRogue,
// Vulkan may work, but not tested.
kAXE,
kAXM,
kAXT,
kBXE,
kBXM, // <-- this device
kBXS,
kBXT,
// First good vulkan drivers.
kCXT,
kDXT,
};kBXM sorts above kBXE, so IsKnownBadDriver() returns false. I walked the rest of the selection chain in android_context_dynamic_impeller.cc to confirm nothing else catches the device:
- not an emulator → passes
ro.com.google.clientidbaseis not Huawei → passes- the MediaTek gate is
vendor_api_level < 32; this device is well above that → passes kBadSocscontains only Exynos boards → passes- a Vulkan context is created and reports valid →
kImpellerVulkan
The cutoff was moved from kCXT to kBXE in #173561, whose description reads: "I've manually tested a BXM device with vulkan and it appears to work well for me. So this changes the threshold where we turn off vulkan access." This report is a counter-example to that single-device validation.
Note also that the enum's own comments still describe BXE–BXT as "Vulkan may work, but not tested" and mark kCXT as "First good vulkan drivers" — the comments and the cutoff currently contradict each other.
Independent evidence that this particular part has unreliable Vulkan drivers: hrydgard/ppsspp#17825 ("Vulkan backend crash after a while on GPU IMG BXM-8-256").
Symptomatically this resembles #159578 and #164606 (glyph atlas), and may share a root cause with #143573 (PowerVR relaxed-precision mismatch).
Suggested fix: restore the cutoff to PowerVRGPU::kCXT, matching what the enum comments already assert, until B-series can be validated more broadly than a single device. Failing that, exclude kBXM specifically.
Code sample
Code sample
// Minimal reproduction for Impeller glyph-atlas corruption on PowerVR BXM.
//
// Usage:
// flutter create bug
// cd bug
// <replace lib/main.dart with this file>
// flutter run --release
//
// Scroll the list up and down a few times on an affected device. Glyphs start
// dropping out of rows that previously rendered correctly — Tamil clusters and
// Latin digits alike. Scrolling a damaged row off-screen and back usually
// repairs it, which is the tell that this is atlas state and not layout.
//
// No fonts are bundled: this uses the platform's own Tamil font, so the bug is
// reproducible without any asset setup. (The app I hit this in bundles Noto
// Sans Tamil and behaves identically, so the specific font is not the factor.)
import 'package:flutter/material.dart';
void main() => runApp(const ReproApp());
class ReproApp extends StatelessWidget {
const ReproApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
appBar: AppBar(title: const Text('Impeller glyph atlas repro')),
body: const _GlyphChurn(),
),
);
}
}
/// Renders a large number of *distinct* glyphs to put the glyph atlas under
/// pressure. Tamil is used because its conjunct/vowel-sign combinations expand
/// into far more unique glyphs than Latin does for the same amount of text,
/// which reaches the failure much faster.
class _GlyphChurn extends StatelessWidget {
const _GlyphChurn();
static const _consonants = 'கஙசஞடணதநபமயரலவழளறன';
static const _vowelSigns = ['', 'ா', 'ி', 'ீ', 'ு', 'ூ', 'ெ', 'ே', 'ை', 'ொ', 'ோ', 'ௌ', '்'];
/// Deterministically builds a different Tamil string for every row, so each
/// row demands glyphs the atlas has not necessarily seen yet.
String _tamilFor(int index) {
final buffer = StringBuffer();
for (var word = 0; word < 4; word++) {
for (var i = 0; i < 3; i++) {
final c = _consonants[(index * 7 + word * 3 + i) % _consonants.length];
final v = _vowelSigns[(index * 5 + word * 2 + i) % _vowelSigns.length];
buffer.write('$c$v');
}
buffer.write(' ');
}
return buffer.toString().trim();
}
@override
Widget build(BuildContext context) {
return ListView.builder(
itemCount: 400,
itemBuilder: (context, index) {
return ListTile(
dense: true,
// Latin digits: these drop out too, which is why this is not a
// Tamil-font problem. Watch for missing digits in these numbers.
leading: Text('${1000 + index}', style: const TextStyle(fontSize: 16)),
title: Text(_tamilFor(index), style: const TextStyle(fontSize: 18)),
subtitle: Text(
'0123456789 · ${index * 37} · ${index * 91}',
style: const TextStyle(fontSize: 14),
),
);
},
);
}
}
Screenshots or Video
Screenshots / Video demonstration
[Upload media here]
https://github.com/user-attachments/assets/46901ccf-32d8-4adc-8f3d-3f9c02d8bf84

Logs
Logs
[Paste your logs here]Flutter Doctor output
Doctor output
[Paste your output here]
Activity
mbcorona commentedon Aug 19, 2026
Hi @ramya-r-ramesh, thanks for the thorough repro, video, and denylist analysis.
Routing to the engine team.
Could you add
flutter doctor -vso we have the exact Flutter/engine version?ramya-r-ramesh commentedon Aug 20, 2026
[√] Flutter (Channel stable, 3.47.0, on Microsoft Windows [Version 10.0.26200.9168], locale en-IN) [596ms]
• Flutter version 3.47.0 on channel stable at C:\Software\flutter
• Upstream repository https://github.com/flutter/flutter.git
• Framework revision 4cf2416 (9 days ago), 2026-08-11 11:53:49 -0700
• Engine revision 5f77625
• Dart version 3.13.0
• 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
[√] Windows Version (Windows 11 or higher, 25H2, 2009) [2.0s]
[√] Android toolchain - develop for Android devices (Android SDK version 36.1.0) [4.2s]
• Android SDK at C:\Users\rames\AppData\Local\Android\sdk
• Emulator version 36.3.10.0 (build_id 14472402) (CL:N/A)
• Platform android-37.0, build-tools 36.1.0
• Java binary at: C:\Program Files\Android\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 21.0.8+-14196175-b1038.72)
• All Android licenses accepted.
[√] Chrome - develop for the web [328ms]
• Chrome at C:\Program Files\Google\Chrome\Application\chrome.exe
[!] Visual Studio - develop Windows apps (SQL Server Management Studio 22 22.1.0) [326ms]
• Visual Studio at C:\Program Files\Microsoft SQL Server Management Studio 22\Release
• SQL Server Management Studio 22 version 22.1.11304.174
X Visual Studio is missing necessary components. Please re-run the Visual Studio installer for the "Desktop
development with C++" workload, and include these components:
MSVC v142 - VS 2019 C++ x64/x86 build tools
- If there are multiple build tool versions available, install the latest
C++ CMake tools for Windows
Windows 10 SDK
[√] Connected device (3 available) [291ms]
• Windows (desktop) • windows • windows-x64 • Microsoft Windows [Version 10.0.26200.9168]
• Chrome (web) • chrome • web-javascript • Google Chrome 151.0.7922.140
• Edge (web) • edge • web-javascript • Microsoft Edge 151.0.4129.93
[√] Network resources [791ms]
• All expected network resources are available.
! Doctor found issues in 1 category.
gaaclarke commentedon Aug 24, 2026
Motorola Moto G64 came out in 2024, I don't think we'd want to block that from vulkan.
ramya-r-ramesh commentedon Aug 25, 2026
Understood — I'm not attached to the blanket cutoff. Would a driver-version gate or a BXM-specific workaround be acceptable instead? Happy to post the exact vkGetPhysicalDeviceProperties values from this device and test an engine build if that helps.
gaaclarke commentedon Aug 25, 2026
Your issue doesn't include what version of flutter you are running against, can you please include that?
Did you try reproducing the problem on the
mainchannel?Did you try reproducing the problem with other devices?
ramya-r-ramesh commentedon Aug 26, 2026
Q1 – Flutter version. State both, with engine revisions so they can map to the source:
Q2 – Did you try reproducing the problem on the main channel?
Not on-device yet — the two Moto G64 units belong to end users, so I can only test through release builds. That said, main carries the same logic: driver_info_vk.cc on main still has powervr_gpu_ < PowerVRGPU::kBXE in IsKnownBadDriver() (line 425 as of today), so a BXM device is routed to Vulkan on every channel. The failure reproduces on 3.38.5, 3.44.9 and 3.47.0 stable, which span the last several engine revisions, and nothing in main changes the PowerVR path other than the Pixel 10 device-ID/driver-version gate
Q3 – Did you try reproducing the problem with other devices?
Two independent users reported it, both on Moto G64 5G (Dimensity 7025 / PowerVR BXM-8-256). We don't have any other PowerVR B-series device to test on. The same app is in production on a wide range of Adreno and Mali devices with no glyph reports, so this appears specific to this GPU/driver rather than to the app.
Denylists b-series powervr from vulkan (flutter#191808)
[CP-beta]Denylists b-series powervr from vulkan (#191904)
[CP-stable]Denylists b-series powervr from vulkan (#191903)
github-actions commentedon Sep 10, 2026
This thread has been automatically locked since there has not been any recent activity after it was closed. If you are still experiencing a similar issue, please open a new bug, including the output of
flutter doctor -vand a minimal reproduction of the issue.