[Impeller/GLES] Recurring SIGSEGV in `libGLESv2_mtk.so` (`glClear`) on raster thread on MediaTek **Android 13** device — please extend the #187404 workaround beyond API 29
- Dominant language
- Dart
- Stars
- 179k
- Forks
- 31.1k
- PR merge metrics
- PR metrics pending
Description
# [UPDATED 2026-07-16] Recurring SIGSEGV in `libGLESv2_mtk.so` on MediaTek Android 13 — root cause is driver job-pool exhaustion, NOT the #187404 idle-context race (workaround extension tested and ineffective)
## Summary
Our production Flutter app crashes repeatedly with a null-pointer SIGSEGV inside the MediaTek vendor GL driver (`/vendor/lib64/egl/libGLESv2_mtk.so`), entered via `glClear` from the Impeller GLES backend on the raster thread.
The crash **signature** is identical to #187336 (fixed by the workaround in #187404), and this issue originally requested extending that workaround's `API <= 29` gate to cover this Android 13 (API 33) device. **We have since tested exactly that (PR #189534, locally built engine, verified via `libflutter.so` BuildId in the crash dump) — it does not fix the crash.** With the workaround verifiably active, the device crashes the same way. Same-day instrumented reproductions show the actual failure mode:
**The driver's internal job pool is exhausted, its job allocation returns null, and the driver's `glClear` path dereferences that null.** Every crash is preceded within ~0.5s by this driver error burst on the crashing thread:
```
E IMGSRV : :1136: RM_GrowJobPool: Number of job pool chunks in use is too large to allow growth
E IMGSRV : :1197: Failed to grow job pool
E IMGSRV : :2800: RM_InsertJob_NoLock: Failed to allocate Job
F libc : Fatal signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x0 in tid ... (1.raster)
```
So while the symptom matches the #187404 family, the mechanism does not: this is job-pool exhaustion under many small GL submissions, not the idle-context `eglMakeCurrent` race. Widening the #187404 gate (or platform list) will not help this device class.
## Evidence (all same device, same day, release builds, full logcat captured)
| engine | context-clear workaround | outcome |
|---|---|---|
| #189534 base (`8a8208292f5`) | off (gated) | crash at ~11 min; crash at ~106 min |
| #189534 head (`ac8d095b843`, local build, BuildId `762f8846...`) | **on (verified active)** | crash at ~86 min; crash at ~22 min |
| experimental mitigation (below) | on | **no crash, zero `IMGSRV` errors, 10+ hours** |
Additional observations:
- 4 job-pool exhaustion bursts landed on the raster thread's `glClear` → all 4 crashed. 1 burst landed on the IO thread (`1.io`, Impeller texture-upload path) → the driver only logged and the process survived. Whether the process dies depends solely on which call site the failed allocation lands on.
- **Trigger state:** every burst occurred while the app sat *idle* on a screen with auto-playing content (banner carousel: periodic image decode → IO-thread texture upload + small animation frames). GL mtrack was a normal ~150–250 MB throughout — not memory pressure. An hour of scripted aggressive scrolling + activity transitions never produced a single `IMGSRV` error.
- Each decoded image currently costs a `glFenceSync` + IO-thread `glFlush` (`image_decoder_impeller.cc` → `ContextGLES::AddTrackingFence`) plus a raster-thread `glWaitSync`; combined with Impeller's per-pass submissions this produces a steady stream of small driver kicks whose retirement seems to fall behind in low-load states, slowly filling the pool.
## Experimental mitigation (validated on-device)
Two changes, gated to `ro.board.platform` starting with `mt67`:
1. `ContextGLES::AddTrackingFence` returns `false` → image uploads fall back to `WaitUntilCompleted` (`glFinish`) on the IO thread, so uploads retire driver jobs immediately instead of accumulating fences.
2. `CommandBufferGLES::OnSubmitCommands` calls `glFinish` every 64th submission → periodically drains the queue so the pool never accumulates.
With this build the device has run clean for 10+ hours under the exact reproduction conditions (and heavier), with zero precursor bursts. A production-quality fix would presumably be finer-grained (cap in-flight fences / batch flushes) — happy to test any variant on this device; with the precursor log as a signal, a build can be validated same-day without waiting for crashes.
## Device
| | |
|---|---|
| Device | OPPO CPH2203 |
| Build fingerprint | `OPPO/CPH2203/OP4F43L1:13/TP1A.220905.001/R.236c2bc+24:user/release-keys` |
| Android version | 13 (API 33) |
| SoC | MediaTek MT6779 (`ro.vendor.mediatek.platform` = `mt6779`, `ro.board.platform` = `mt6779`) |
| GPU | Imagination PowerVR Rogue GM9446, OpenGL ES 3.2 build 1.13@5776728 (Vulkan not used → Impeller falls back to the OpenGLES backend) |
| GL driver | `libGLESv2_mtk.so`, BuildId `5f0230c5c21f5c7f1d1c30e576a5e1fd` |
## Flutter version (production crashes)
```
Flutter 3.44.5 • channel stable
Framework • revision f94f4fc76b (2026-07-06)
Engine • hash d3a3293399556a85388faf8c6f0723a7a5597aa8 (revision 83675ed276)
Tools • Dart 3.12.2
```
Release AOT build (arm64-v8a), default renderer settings. (Reproduction/testing above was done on master `8a8208292f5` and PR #189534.)
## Raw backtrace (production occurrence; identical shape in all reproductions)
```
signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x0000000000000000
Cause: null pointer dereference
pid: 5460, tid: 8782, name: 1.raster >>> com. <<<
backtrace:
#00 pc 000000000019e794 /vendor/lib64/egl/libGLESv2_mtk.so (BuildId: 5f0230c5c21f5c7f1d1c30e576a5e1fd)
#01 pc 00000000000f737c /vendor/lib64/egl/libGLESv2_mtk.so (BuildId: 5f0230c5c21f5c7f1d1c30e576a5e1fd)
#02 pc 00000000000afab4 /vendor/lib64/egl/libGLESv2_mtk.so (BuildId: 5f0230c5c21f5c7f1d1c30e576a5e1fd)
#03 pc 00000000000b0150 /vendor/lib64/egl/libGLESv2_mtk.so (glClear+564) (BuildId: 5f0230c5c21f5c7f1d1c30e576a5e1fd)
#04 pc 00000000008197d8 libflutter.so (BuildId: 9f490a28849fd415f77ad9165383b441bf3cd3bc)
#05 pc 0000000000818a44 libflutter.so
#06 pc 00000000007de65c libflutter.so
#07 pc 00000000007e3ac4 libflutter.so
#08 pc 00000000008d7c3c libflutter.so
#09 pc 0000000000764168 libflutter.so
#10 pc 0000000000852690 libflutter.so
#11 pc 0000000000852084 libflutter.so
#12 pc 0000000000853398 libflutter.so
#13 pc 0000000000852eac libflutter.so
#14 pc 000000000085fba8 libflutter.so
#15 pc 00000000004e9dbc libflutter.so
#16 pc 00000000004edcb0 libflutter.so
#17 pc 000000000001836c /system/lib64/libutils.so (android::Looper::pollInner(int)+1068)
#18 pc 0000000000017ee0 /system/lib64/libutils.so (android::Looper::pollOnce(int, int*, int*, void**)+112)
#19 pc 00000000000185e4 /system/lib64/libandroid.so (ALooper_pollOnce+100)
#20 pc 00000000004eddb4 libflutter.so
#21 pc 00000000004ebcfc libflutter.so
#22 pc 00000000004ebb0c libflutter.so
#23 pc 00000000000eb720 libc.so (__pthread_start(void*)+208)
#24 pc 000000000007e2d0 libc.so (__start_thread+64)
```
## Symbolized engine frames
```
#04 0x8197d8 impeller::EncodeCommandsInReactor(...) render_pass_gles.cc:300
→ GLProc glClear proc_table_gles.h:138
#05 0x818a44 impeller::ReactorGLES::ReactOnce() / FlushOps() reactor_gles.cc:293
#06 0x7de65c impeller::Canvas::EndReplay() canvas.cc:2393
#07 0x7e3ac4 impeller::RenderToTarget(...) / FinishRecording() dl_dispatcher.cc:1326
#08 0x8d7c3c flutter::GPUSurfaceGLImpeller::AcquireFrame(...)::$_1 gpu_surface_gl_impeller.cc:119
#09 0x764168 std::function::operator()
#10 0x852690 flutter::Rasterizer::DrawToSurfaceUnsafe(...) rasterizer.cc:811
#11 0x852084 flutter::Rasterizer::DrawToSurfaces(...) rasterizer.cc:603
#12 0x853398 flutter::Rasterizer::DoDraw(...) rasterizer.cc:507
#13 0x852eac flutter::Rasterizer::Draw(...) rasterizer.cc:264
#14 0x85fba8 flutter::Shell::OnAnimatorDraw(...)::$_0 shell.cc:1412
#15 0x4e9dbc fml::MessageLoopImpl::FlushTasks(...) message_loop_impl.cc:126
#16 0x4edcb0 fml::MessageLoopAndroid::OnEventFired() message_loop_android.cc:91
```
## Related
- #189534 — extends the #187404 workaround gate to this device; **tested on-device (local engine build) and does not fix the crash**
- #187336 / #187404 — same crash signature, but a different mechanism (idle-context race vs. job-pool exhaustion); suggest treating these as separate MediaTek/PowerVR issue families
- #166248, #188560, #169500 — other MediaTek EGL crashes that may be worth re-triaging with the job-pool signature in mind (`RM_GrowJobPool` in logcat is the discriminator)
## Request
1. Reconsider the approach in #189534: on this device class the fix direction that works is **reducing GL submission pressure** (per-upload fence throttling / batching, periodic queue drain), not context clearing. Our experimental mitigation above validates the direction.
2. When triaging MediaTek `libGLESv2_mtk.so` crashes, check for the `RM_GrowJobPool` / `Failed to allocate Job` logcat precursor to distinguish this family from the #187404 race.
We have the device on hand, a reliable reproduction (idle on a screen with auto-playing carousel content; crash in 11–106 min, precursor log visible sooner), and a local engine build setup — we can validate any experimental fix same-day.
Contributor guide
Research direction
Start with image_decoder_impeller.cc and the ContextGLES::AddTrackingFence and CommandBufferGLES::OnSubmitCommands entry points, then reproduce on the OPPO CPH2203 while watching logcat for RM_GrowJobPool and Failed to allocate Job. Compare a submission-throttling or queue-drain change against the reported experimental mitigation. Done means the reliable idle-carousel reproduction runs without precursor errors or SIGSEGV, while the existing context-clear workaround is not treated as the fix.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- android, cpp
- Domain
- computer-graphics, mobile-dev, performance
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100