software-mansion / software-mansion/react-native-executorch

Gemma 4 multimodal on Vulkan starves the display for minutes on PowerVR (Pixel 10)

Open
#1,475 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

platform: android user exp
Dominant language
TypeScript
Stars
1.7k
Forks
96
Avg merge
20h 51m
Merged PRs (30d)
59

Description

Description

One image through gemma4-e2b-multimodal — the Vulkan export, which
GEMMA4_E2B_MM.modelSource selects on Android — held the GPU for 12 minutes
without emitting a token
on a Pixel 10 (PowerVR D-Series DXT-48-1536, driver
25.3@6908880), with the device unable to present a frame the whole time. I
force-stopped it; it had not finished. Export tag and runtime match, and the
file was deleted and re-downloaded before the run.

export input result
gemma4-e2b-mm vulkan text {"answer": "Blue, green, and red."} — seconds
gemma4-e2b-mm vulkan one 768×768 image no output in 12 min, display frozen
lfm2.5-vl-450m xnnpack same image "A circle is in the image. The color is red." — seconds
The display is starved; the GPU is not hung

GPU held a steady ~1.29 W throughout. The app's clock froze at 12:27 while the
system clock read 12:39; Choreographer: Skipped ~480 frames! every four
seconds; Fence: waitForever … drv(pvrsrvkm) status(0); and screencap started
at 12:31 returned only at 12:37, with a frame still showing 12:27.
Force-stopping the app unfroze the display at once. The same model answers an
image on its XNNPACK export in ~1 min 48 s.

One OS build earlier, this rebooted the phone

On Android 16, dropbox and the kernel log:

crash reason: powervr - [11957]:[ion.privatemind]:[GUILTY_OVERRUNING]
pvrsrvkm 34f00000.gpu0: GPU reset reason=GUILTY_OVERRUNING pid=5037

48 Compute dispatch commands queued, the display fence never fired, then
system_server_watchdog ×2 and SYSTEM_RESTART. The ANR trace on thread
RN_ET_Worker0 sat in IMG_vkWaitForFences under:

vkcompute::ComputeGraph::execute
executorch::extension::llm::MultimodalPrefiller::prefill
rnexecutorch::models::llm::LLM::generateMultimodal

That build also returned corrupted output when it returned at all:
インチ is position is, receivable, denn, is is is is is

A lead: your bundled ExecuTorch predates the PowerVR fix

pytorch/executorch#17299 — same GPU, same device class — was root-caused to
ComputeGraph::prepack() batching prepack dispatches into one command buffer,
where on PowerVR only the first prepacked constant gets correct data. Fixed by
pytorch/executorch#22147 (2026-08-26, tested on a Pixel 10), which deleted
glsl/no_op.glsl and added Context::register_barrier.

Both your snapshots predate it — 0.9.2's libexecutorch.so and
v0.10.0-libs' libvulkan_executorch_backend.so, which 0.10.2 pins via
nativeLibsVersion. In each, strings | grep -c no_op is 18 and
nm -DC | grep -c register_barrier is 0, against 18 other barrier
symbols, so the table is not stripped; no_op.glsl is gone from ExecuTorch
main. Upgrading to 0.10.2 does not pick the fix up.

Not a claim of causation — and one fact cuts against it: this driver is the one
on which #17299's reporter stopped reproducing, though their models were 5–6 MB,
under prepack_threshold_nbytes, so prepack() never split the buffer. This
one is 4 GB.

What would help
  1. Run a VLM image prompt on any PowerVR device against current main.
  2. Rebuild the Vulkan artifacts against an ExecuTorch containing #22147 — the
    only known PowerVR fix absent from your binaries.
  3. Look separately at GPU allocation lifetime (see performance logs).
  4. Until then, consider not defaulting Android to a Vulkan multimodal export.
Not established

Whether the shipped LFM2_5_VL_*.VULKAN_8DA4W exports behave the same: they
exist only from v0.10.0, so a failure on the 0.9.2 runtime would not separate
PowerVR from a backend too old for a newer export — though a v0.10.0 XNNPACK
export does load and answer correctly there. Whether the prefill ever finishes,
or always ends in a reset, is also unknown, and nothing is logged: the stall is
silent.

Steps to reproduce
  1. On a PowerVR device (Pixel 10 / Tensor G5 here), load GEMMA4_E2B_MM — on
    Android it resolves to the Vulkan export
    gemma-4-multimodal/resolve/v0.9.0/e2b/vulkan/gemma_4_e2b_vulkan_8da4w.pte
  2. Wait for the model to finish loading.
  3. Send a text-only prompt ("Name three colours."). It answers in seconds, so
    the model and the Vulkan backend work.
  4. Send one image (a 768×768 PNG of a red circle on white sufficed) with
    "What shape and colour is in this image?"
  5. Watch the device, not the app: the UI stops updating within seconds,
    screencap blocks for minutes and returns a stale frame, and dumpsys gpu
    shows the app holding ~3.6 of 4.0 GB of GPU.
  6. No token appears. After 12 minutes am force-stop recovers the display.
Snack or a link to a repository

https://github.com/software-mansion-labs/private-mind

React Native Executorch version

0.9.2

React Native version

0.83.4

Platforms

Android

JavaScript runtime

No response

Workflow

React Native

Architecture

No response

Build type

No response

Device

Real device

Device model

Google Pixel 10 / Tensor G5 (Android 17, CP2A.260805.005)

AI model

gemma4-e2b-multimodal, Vulkan export (tag v0.9.0)

Performance logs

GPU power held a steady ~1.29 W (S2S_VDD_GPU) for all 12 minutes — working,
not deadlocked. dumpsys gpu during the stall:

Memory snapshot for GPU 0:
Global total:      4007948288
Proc 11331 total:  3602255872     <- the app
Proc 610 total:     131170304     <- surfaceflinger

The app holds 3.6 GB of a 4.0 GB global GPU budget — the whole model is resident
on the GPU, consistent with the display starving rather than the GPU hanging.

The allocation outlives the process: after am force-stop, with pidof
returning nothing, the snapshot still reports those 3.6 GB against the dead PID.

Acknowledgements

Yes

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the listed 768×768 image reproduction using GEMMA4_E2B_MM and capture GPU, display, and allocation behavior. Compare the shipped Vulkan artifacts and current main against the ExecuTorch build containing #22147, including the prepack and barrier symbols mentioned. Done means the image prompt completes without display starvation, reset, corrupted output, or allocations persisting after force-stop.

Written by the indexing model from the issue text.

Assessment

Tech stack
android, react-native, typescript
Domain
ai, computer-vision, mobile, performance
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.