Skip to content

[Impeller][Android][GLES] FragmentProgram causes SIGABRT on second app launch — "Must be able to encode GL commands without error" #186186

Open

Description

@heyi1994

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

  1. Create a Flutter app that uses ui.FragmentProgram.fromAsset() to load a custom
    fragment shader declared under shaders: in pubspec.yaml.
  2. Run flutter run --release on an Android device (Impeller GLES enabled by default).
  3. First launch: app runs correctly, shader renders as expected.
  4. Force-kill the app (swipe away from recents or adb shell am force-stop).
  5. Relaunch the app.
  6. 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

  1. added
    c: crashStack traces logged to the console
    platform-androidAndroid applications specifically
    engineflutter/engine related. See also e: labels.
    e: impellerImpeller rendering backend issues and features requests
    workaround availableThere is a workaround available to overcome the issue
    fyi-androidFor the attention of Android platform team
    and removed
    c: crashStack traces logged to the console
    on May 7, 2026
  2. mbcorona commented on May 7, 2026

    @mbcorona
    Member

    Hi @heyi1994 thanks for the detailed report and providing the root cause analysis, I am routing to the engine for further investigation.

  3. jason-simmons commented on May 8, 2026

    @jason-simmons
    Contributor

    Tried running some apps that use FragmentProgram.fromAsset with 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.

  4. heyi1994 commented on May 9, 2026

    @heyi1994
    Author

    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:

    #include <flutter/runtime_effect.glsl>
     out vec4 fragColor;
    
     void main() {
        fragColor = vec4(0.0,0.0,0.0,1.0);
    }
    
    import 'dart:ui' as ui;
    import 'package:flutter/material.dart';
    
    void main() => runApp(
    const MaterialApp(
      home: Scaffold(
        body: Column(
          children: [
            ShaderWidget(path: 'shaders/simple.frag'),
            ShaderWidget(path: 'shaders/simple.frag'),
          ],
        ),
      ),
    ),
    );
    
    class ShaderWidget extends StatefulWidget {
    final String path;
    
    const ShaderWidget({super.key, required this.path});
    
    @override
    State<ShaderWidget> createState() => _ShaderWidgetState();
    }
    
    class _ShaderWidgetState extends State<ShaderWidget>
      with SingleTickerProviderStateMixin {
    ui.FragmentShader? _shader;
    
    @override
    void initState() {
      super.initState();
      _loadShader();
    }
    
    Future<void> _loadShader() async {
      final program = await ui.FragmentProgram.fromAsset(widget.path);
      if (!mounted) return;
      setState(() => _shader = program.fragmentShader());
    }
    
    @override
    void dispose() {
      _shader?.dispose();
      super.dispose();
    }
    
    @override
    Widget build(BuildContext context) {
      if (_shader == null) return const SizedBox(width: 200, height: 100);
      return CustomPaint(
        painter: _SimplePainter(_shader!),
        child: const SizedBox(width: 200, height: 100),
      );
    }
    }
    
    class _SimplePainter extends CustomPainter {
    final ui.FragmentShader shader;
    
    const _SimplePainter(this.shader);
    
    @override
    void paint(Canvas canvas, Size size) =>
        canvas.drawRect(Offset.zero & size, Paint()..shader = shader);
    
    @override
    bool shouldRepaint(_SimplePainter old) => false;
    }
                       

    Bisection results (force-stop → relaunch methodology)

    Configuration Crashes?
    1× simple.frag No
    2× simple.frag (same path) Yes
    2× separate files (identical content, different paths) No
    1× complex shader (48 uniforms) No
    2× complex shader (same path) Yes
    complex shader + copy at different path No

    Root cause

    FragmentProgram.fromAsset() has a static cache keyed by asset path. Two
    widgets calling it with the same path receive the same FragmentProgram
    instance
    . Both then call program.fragmentShader(), producing two
    FragmentShader objects that share the same underlying GL program ID in
    Impeller 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_binary disk cache. When Impeller submits two draw calls
    in 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) or FML_CHECK(result) failure in
    render_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

    • Blacklist GL_IMG_program_binary on PowerVR in Impeller GLES (similar to
      the existing IsKnownBadDriver() mechanism), or
    • Ensure each fragmentShader() call produces an independent GL program
      object rather than sharing one across all FragmentShader instances from the
      same FragmentProgram

    Device info:

    • Device: OPPO CPH2203
    • Build:
      OPPO/CPH2203/OP4F43L1:13/TP1A.220905.001/R.206769c-2:user/release-keys
    • Android: 13 / ABI: arm64
    • GPU: Imagination Technologies, PowerVR Rogue GM9446
    • OpenGL ES: 3.2 (build 1.13@5776728) — ro.opengles.version: 196610
  5. added
    P2Important issues not at the top of the work list
    on May 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Important issues not at the top of the work liste: impellerImpeller rendering backend issues and features requestsengineflutter/engine related. See also e: labels.platform-androidAndroid applications specificallyteam-engineOwned by Engine teamtriaged-engineTriaged by Engine teamworkaround availableThere is a workaround available to overcome the issue

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions