Repository navigation
Description
Description
Using ui.FragmentProgram.fromAsset() with Impeller's OpenGL ES backend on Android
causes a native SIGABRT crash on the second cold launch of the app. The first
launch succeeds, but after killing the process and relaunching, the raster thread
aborts with a fatal GL encoding error.
Steps to Reproduce
- Create a Flutter app that uses
ui.FragmentProgram.fromAsset()to load a custom
fragment shader declared undershaders:inpubspec.yaml. - Run
flutter run --releaseon an Android device (Impeller GLES enabled by default). - First launch: app runs correctly, shader renders as expected.
- Force-kill the app (swipe away from recents or
adb shell am force-stop). - Relaunch the app.
- Crash on second launch.
Expected Behavior
App launches successfully on every cold start. Custom fragment shaders work
consistently with Impeller GLES across multiple launches.
Actual Behavior
Second launch crashes with SIGABRT on the raster thread:
pid: 28544, tid: 28674, name: 1.raster >>> com.example.test111 <<<
signal 6 (SIGABRT), code -1 (SI_QUEUE)
Abort message: '[FATAL:flutter/impeller/renderer/backend/gles/render_pass_gles.cc(692)]
Check failed: result. Must be able to encode GL commands without error.'
Full native stack trace
Build fingerprint: 'OPPO/CPH2203/OP4F43L1:13/TP1A.220905.001/R.206769c-2:user/release-keys'
ABI: 'arm64'
#00 pc 000000000007caa4 /apex/com.android.runtime/lib64/bionic/libc.so (abort+180)
#1 pc 00000000004dbfa4 libflutter.so
#2 pc 00000000004dbf2c libflutter.so
#3 pc 0000000000801abc libflutter.so
#4 pc 0000000000800458 libflutter.so
#5 pc 00000000007c6e7c libflutter.so
#6 pc 00000000007cc120 libflutter.so
#7 pc 00000000008b468c libflutter.so
#8 pc 000000000074f0e4 libflutter.so
#9 pc 0000000000839840 libflutter.so
#10 pc 0000000000839210 libflutter.so
#11 pc 000000000083a548 libflutter.so
#12 pc 000000000083a048 libflutter.so
#13 pc 000000000084898c libflutter.so
#14 pc 00000000004dcb2c libflutter.so
#15 pc 00000000004e0a28 libflutter.so
#16 pc 000000000001836c /system/lib64/libutils.so (android::Looper::pollInner+1068)
#17 pc 0000000000017ee0 /system/lib64/libutils.so (android::Looper::pollOnce+112)
#18 pc 00000000000185e4 /system/lib64/libandroid.so (ALooper_pollOnce+100)
#19 pc 00000000004e0b2c libflutter.so
#20 pc 00000000004dea38 libflutter.so
#21 pc 00000000004de848 libflutter.so
#22 pc 00000000000eb720 /apex/com.android.runtime/lib64/bionic/libc.so (__pthread_start+208)
#23 pc 000000000007e2d0 /apex/com.android.runtime/lib64/bionic/libc.so (__start_thread+64)
Root Cause Analysis
Impeller GLES appears to cache compiled pipeline state objects (PSOs) to disk after
the first launch. On the second cold launch, a new GL context is created, but Impeller
attempts to reuse the cached pipeline with the old context's program handles. These
handles are invalid in the new context, causing GL command encoding to fail fatally.
Because the crash is a native CHECK failure inside render_pass_gles.cc, it cannot
be caught by Dart's try/catch — FragmentProgram.fromAsset() itself succeeds, and
the abort happens later in the raster thread during the actual draw call.
Workaround
Disable Impeller on Android in AndroidManifest.xml:
<meta-data
android:name="io.flutter.embedding.android.EnableImpeller"
android:value="false" />With Skia as the backend, FragmentProgram works correctly across all launches.
Flutter / Dart Version
Flutter 3.41.9 • channel stable
Framework • revision 00b0c91
Engine • hash 9161402dc0e134b3fb5adee5046b6e84b1a5e1c1
Dart 3.11.5 • DevTools 2.54.2
Device Info
- Device: OPPO CPH2203 (OP4F43L1)
- Android: 13 (TP1A.220905.001)
- ABI: arm64
- Renderer: Impeller GLES (default on this Flutter version)
Vulkan Info
adb shell pm list features | grep -i vulkan
feature:android.hardware.vulkan.compute
feature:android.hardware.vulkan.level
feature:android.hardware.vulkan.version=4198400
feature:android.software.vulkan.deqp.level=132383489
Checklist
- I have searched existing issues and this is not a duplicate
- I can reproduce this on a physical device
- This crash does not occur with EnableImpeller=false
- This crash does not occur on the first cold launch, only subsequent ones
Activity
mbcorona commentedon May 7, 2026
Hi @heyi1994 thanks for the detailed report and providing the root cause analysis, I am routing to the engine for further investigation.
jason-simmons commentedon May 8, 2026
Tried running some apps that use
FragmentProgram.fromAssetwith Impeller/GLES but was unable to reproduce this.Do you have a simple app that reproduces it? And does it only happen on specific devices?
The Impeller Vulkan back end writes a pipeline cache to disk (see https://github.com/flutter/flutter/blob/master/engine/src/flutter/impeller/renderer/backend/vulkan/pipeline_cache_data_vk.cc). But the GLES back end is not doing that.
heyi1994 commentedon May 9, 2026
Update: Root cause identified via bisection
After extensive bisection on the affected device (OPPO CPH2203, PowerVR Rogue
GM9446, Android 13), the crash has been reduced to a minimal reproducer
and the root cause identified.
Minimal reproducer
The crash requires only a trivial shader (
simple.frag) with no uniforms —shader complexity is not the factor. The only condition that triggers the
crash is having two widgets that each call
FragmentProgram.fromAsset()with the same asset path, running simultaneously:
Bisection results (force-stop → relaunch methodology)
simple.fragsimple.frag(same path)Root cause
FragmentProgram.fromAsset()has a static cache keyed by asset path. Twowidgets calling it with the same path receive the same
FragmentPrograminstance. Both then call
program.fragmentShader(), producing twoFragmentShaderobjects that share the same underlying GL program ID inImpeller GLES.
On first launch the GL program is compiled fresh — no crash. On the second
launch (after force-stop), the PowerVR driver restores the program from its
GL_IMG_program_binarydisk cache. When Impeller submits two draw callsin the same frame both using the same cached GL program ID, the PowerVR driver
crashes with a use-after-free (SIGSEGV, fault addr
0x000192010300b821,x20=
0x220192010300b801) orFML_CHECK(result)failure inrender_pass_gles.cc:692.Clearing app data resolves the crash until the next force-stop cycle, because
it clears the driver-level binary cache.
Workaround
Use a different file path for each concurrent widget instance (even with
identical content). This gives each widget an independent GL program ID.
Suggested fix
GL_IMG_program_binaryon PowerVR in Impeller GLES (similar tothe existing
IsKnownBadDriver()mechanism), orfragmentShader()call produces an independent GL programobject rather than sharing one across all
FragmentShaderinstances from thesame
FragmentProgramDevice info:
OPPO/CPH2203/OP4F43L1:13/TP1A.220905.001/R.206769c-2:user/release-keys