fix(android): defer first frame to reduce splash freeze on Xiaomi/MediaTek#1107
Closed
SERDUN wants to merge 4 commits into
Closed
fix(android): defer first frame to reduce splash freeze on Xiaomi/MediaTek#1107SERDUN wants to merge 4 commits into
SERDUN wants to merge 4 commits into
Conversation
There was a problem hiding this comment.
Pull request overview
Defers Flutter’s first frame during startup initialization to reduce intermittent one-frame splash freezes on certain Android devices (Xiaomi/MediaTek) affected by Impeller Vulkan format probing/fallback behavior.
Changes:
- Capture
WidgetsFlutterBindingand calldeferFirstFrame()during startup. - Wrap async
bootstrap()+ Crashlytics setup intry/finallyto ensureallowFirstFrame()is always called. - Add an in-code rationale comment describing the Impeller/Gralloc motivation.
Comment on lines
+29
to
+33
| final binding = WidgetsFlutterBinding.ensureInitialized(); | ||
| // Defer the first frame until after async initialization completes. | ||
| // This reduces the risk of a one-frame splash freeze on devices with | ||
| // non-standard Gralloc HALs (e.g. Xiaomi/MediaTek) where the Impeller | ||
| // Vulkan capability probe fails and falls back — a process that competes |
| final instanceRegistry = await bootstrap(); | ||
|
|
||
| if (!kIsWeb && kDebugMode) { | ||
| FirebaseCrashlytics.instance.setCrashlyticsCollectionEnabled(false); |
Comment on lines
+47
to
+51
| FlutterError.onError = (details) { | ||
| logger.severe('FlutterError', details.exception, details.stack); | ||
| if (!kIsWeb && !kDebugMode) { | ||
| FirebaseCrashlytics.instance.recordFlutterFatalError(details); | ||
| } |
Comment on lines
+27
to
+31
| runZonedGuarded( | ||
| () async { | ||
| WidgetsFlutterBinding.ensureInitialized(); | ||
|
|
||
| final instanceRegistry = await bootstrap(); | ||
|
|
||
| if (!kIsWeb && kDebugMode) { | ||
| FirebaseCrashlytics.instance.setCrashlyticsCollectionEnabled(false); | ||
| await FirebaseCrashlytics.instance.deleteUnsentReports(); | ||
| } | ||
|
|
||
| FlutterError.onError = (details) { | ||
| logger.severe('FlutterError', details.exception, details.stack); | ||
| if (!kIsWeb && !kDebugMode) { | ||
| FirebaseCrashlytics.instance.recordFlutterFatalError(details); | ||
| final binding = WidgetsFlutterBinding.ensureInitialized(); | ||
| // Defer the first frame until after async initialization completes. | ||
| // This reduces the risk of a one-frame splash freeze on devices with |
…iaTek On devices with non-standard Gralloc HALs (e.g. Xiaomi, MediaTek), the Impeller Vulkan backend probes for format 0x38 during engine startup. The probe fails with EINVAL and falls back to a compatible format — but this fallback races with the first vsync signal, occasionally causing a one-frame freeze on the splash screen. Deferring the first Flutter frame until async bootstrap completes gives the raster thread time to finish the probe and swapchain fallback before any frame is submitted for rasterization, reducing the risk of the visible stutter. Related upstream issue: flutter/flutter#182808
Wraps the async init block in try/finally so binding.allowFirstFrame() is called on both success and error paths. Previously, if bootstrap() or any subsequent init threw, the runZonedGuarded error handler would log the error but allowFirstFrame() would never be called — leaving the app stuck on the native launch theme indefinitely.
…setup - Wrap deferFirstFrame/allowFirstFrame in a `deferFrame` flag so the delay applies only on Android, where the Impeller Vulkan format-0x38 probe race exists. iOS/web/desktop are unaffected and no longer incur the deferred-frame path. - Add missing `await` before setCrashlyticsCollectionEnabled so it completes before deleteUnsentReports is called.
915dffd to
b83d6ce
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
On devices with non-standard Gralloc HALs (Xiaomi, MediaTek), the Impeller Vulkan backend probes for pixel format
0x38(56) during engine startup:```
E Failed to allocate (4 x 4) layerCount 1 format 56 usage b00: 5
E GraphicBuffer(w=4, h=4, lc=1) failed (Unknown error -5), handle=0x0
```
The probe fails with
EINVALand Impeller falls back to a compatible format. This fallback runs on the raster thread and races with the first vsync signal — occasionally causing a one-frame freeze on the splash screen (intermittent, device-specific).Root cause confirmed in upstream Flutter issue: flutter/flutter#182808 (Flutter 3.41.2, Vivo/MediaTek, identical errors).
Fix
Use
WidgetsBinding.deferFirstFrame()/allowFirstFrame()to hold the first Flutter frame until asyncbootstrap()completes. During that time (~100–500ms of Firebase/prefs init), the raster thread finishes the Impeller probe and swapchain fallback — so by the time the first frame is submitted for rasterization, Impeller is already ready.Side effect (positive):
FlutterActivitykeeps the native LaunchTheme visible untilallowFirstFrame()is called, making the splash-to-UI transition smoother.Changes
lib/main.dart: capturebindingreference, adddeferFirstFrame()afterensureInitialized(), addallowFirstFrame()beforerunApp()Test
Verify on a Xiaomi or MediaTek device that the one-frame splash freeze no longer occurs on app launch.