You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
ANR on AG-216: RenderThread blocked in eglSwapBuffers with 8 multi-view SurfaceViews #76
The app's own threads were idle. The compositor did not accept the window's next buffer for about 40 s. Before that, the screen showed a frozen frame while input was still queued.
Context: SurfaceFlinger used about 40 % CPU and the box load average was up to 30. Other apps (Play Store ANRs, a video app, Settings) were active on the same box at the time, so the box may simply have been overloaded. With TextureView cells (before #63) the app gave SurfaceFlinger one layer; with SurfaceView cells it gives up to 8 more.
To do (deferred): reproduce on an idle box, comparing 4 and 8 cells and TextureView with SurfaceView. If the extra layers are the cause, consider TextureView for the 3×3 grid on devices with few HWC overlay planes, or fewer layer changes when leaving the grid.
Seen a third time, 2026-10-13 14:13, Aceline-1 (build 0242d14), an otherwise idle box (Wi‑Fi off, no other apps in use), while switching slots 2 and 3 to UDP through the URL dialog: 3×3 grid (8 cells + U, all SurfaceView) → tap slot 2 (grid stopped, single view with its TextureView) → Settings dialog → UDP → Save → menu → tap 3.
OpenGLRenderer: Davey! duration=48624ms … QueueBufferDuration=48550217000 — the RenderThread waited 48.5 s in queueBuffer, i.e. SurfaceFlinger did not take the window's buffer; Choreographer: Skipped 2914 frames.
am_anr: Input dispatching timed out … Wait queue head age: 42159.8 ms; load average 8.8 (1 min) — lower than the earlier cases, so an overloaded box is not required.
The Java traces were written a second after the RenderThread recovered, so they show it idle and main in TextureView.onFrameAvailable.
Common to all three cases: the window's layer set changes a lot at once (the grid of up to 9 SurfaceViews torn down, the TextureView of the single view and a dialog or popup appearing). Next step (planned today after #113): reproduce with a script (grid ↔ single ↔ dialog in a loop) and compare with fewer SurfaceViews / hiding the grid before removing it.
Status 2026-10-13 15:35 — reproducible, cause not found yet (time-boxed).
Repro script on Aceline-1 (idle box, Wi‑Fi off): menu → slot 4 (grid → single), 4 s, menu → Q (single → grid), 8 s; repeat. Every run of 8–10 cycles gives 2–5 Davey! stalls of 3–68 s (QueueBufferDuration = the whole stall, i.e. SurfaceFlinger does not take the window's buffer) and usually an ANR; the longest ones come on single → grid. Once media.codec hung in D state (load 74) and only a reboot helped.
One more data point (2026-10-13 16:55, Aceline-1, 6 cycles grid ↔ single): stalls of 24.5 s and 20.9 s again, but dumpsys SurfaceFlinger answered every ~4 s throughout them — SurfaceFlinger itself is not blocked. The window's own queueBuffer waits, i.e. the app's BufferQueue to SurfaceFlinger (a buffer not released back / a fence not signalled) rather than a frozen compositor. Next capture: dumpsys SurfaceFlinger --latency <app layer> and the app window's BufferQueue state during a stall, plus a run with the grid hidden (not removed) on single view.
grid views kept and reused, no removeAllViews + rebuild (experiment)
40184, 57024, 57046
2
S on: every cell on key frames (~1 fps each), single view 1 fps too
5699, 5653, 4068, 52903, 52816
4
So neither creating/destroying the 9 SurfaceView layers nor the decode/update rate is the cause.
Captured during a stall (dumpsys SurfaceFlinger, grid on screen): 8 of the 9 SurfaceView layers are composed by the GPU (CLIENT), one goes to an HWC overlay (DEVICE); the 1920×1080 / 2560×1920 YUV sources are scaled to 426×240 by Mali-G31 composition. surfaceflinger ~42 % CPU, load 6.9, 216 MB RAM free. The app window's own buffer still waits in queueBuffer for tens of seconds.
Next ideas (not tried):
TextureView (or one GL surface) for the grid — the app composes the cells itself, SurfaceFlinger gets one layer (Quad: frequent blocking GC with four streams #61 found 1 MB/s of GC garbage with TextureView frame callbacks; would need a callback-free path).
A 2×2 grid against 3×3 on the same box (layer count only).
Test on another device class (the stalls may be specific to this Mali/HWC driver).
Time-boxed here; back to deferred.
Status 2026-10-14: not reproducible right after a reboot.
Aceline-1 (.144), v2.01, rebooted at ~15:45 MSK (it had been up ~21 h; at that point cells 6–8 were stuck at Paused: decoder overloaded even on key frames). Same repro as yesterday: grid → slot 4 single (4 s) → Q grid (8 s), 8 cycles, driven by D-pad. Every cycle rebuilt the grid (checked in logcat).
run (box up ~1 h)
stalls ≥ 2 s
ANR
3×3, slot 2 = HTTP MJPEG
one, 3.1 s
0
3×3, slot 2 = RTSP H.264 (yesterday's layout)
none
0
Yesterday the same script gave stalls of 40–65 s on every run. Those runs came after long test sessions, on the same day as the decoder hang (#145).
New hypothesis: the stalls come from the box degrading with uptime or use (GPU/codec driver memory), not from the grid switch itself. Running now: a soak test on .144 (v2.02) that keeps the 3×3 grid playing and, every 40 min, logs /proc/meminfo, the app's graphics memory and an 8-cycle repro. This will show whether the stalls come back as uptime grows.
Status 2026-10-14 evening: the stalls build up inside the app process; restarting the app clears them.
Aceline-1 (.144), v2.02, same repro (8 cycles of grid → slot 4 single → grid, D-pad):
state
stalls ≥ 2 s (ms)
ANR
box rebooted, app fresh (~16 cycles)
3128, then none
0
soak, box up 63 min (~40 cycles)
10028 6390 6382 3481 2101
0
soak, box up 116 min (~48 cycles)
20069 18076 18104 18171 29032 2032
0
app force-stopped and started, box not rebooted (box up 2 h)
3048
0
the same app process, 16 more cycles
22167 50790 3409 50955 50914
2
So whatever builds up belongs to the app process, not to the box: a restart of the app alone brings it back to the clean state.
What does not grow with the cycles:
threads (76–79);
native heap (~355 MB allocated right after start, the same later; that is the 9 decoders);
SurfaceFlinger layers (exactly 9 SurfaceView layers in the grid) and gralloc buffers (~100, about 11 per cell);
decoder instances (dumpsys media.resource_manager: 9 codecs for the live process; dead PIDs are listed with no clients);
hwui GPU caches. They depend on the view, not on the cycles: in the grid 2 textures (9 MB), in the single view 13 (97 MB = the TextureView's 1920×1088 frame buffers), released again on the way back.
Not visible without root: the Mali driver's per-process GPU memory, and the app window's BufferQueue (the stall is the window's own queueBuffer).
Next (another day):
Single view on a SurfaceView instead of the TextureView. The TextureView's frames go through the app's own GL composition and its window buffer, which is the buffer that waits.
Whether the time per cycle grows from the first switch on: per-cycle Davey! durations in one long run after an app restart.
Status 2026-10-15: a SurfaceView for the single view removes the stalls.
Aceline-1 (.144), box up ~30 min, same 3×3 layout. Each run starts from a fresh app process and does 60 cycles of grid → slot 4 single (4 s) → grid (8 s), driven by D-pad. Davey! lines of the app process are counted per cycle.
experiment (7938365, branch experiment/surfaceview-76), single view on a SurfaceView
1–30
2
2
0
1.4 s
0
31–60
2
2
0
1.3 s
0
With the TextureView the stalls grow with the cycles in one process, as seen yesterday: up to 14 s within 60 cycles. With the SurfaceView there are almost no stalls and no growth. So the build-up is in the TextureView path, where frames go through the app's own GL composition and window buffer.
The experiment is not ready to merge. It loses three things that need a proper replacement:
Pinch-zoom and pan in single view: a SurfaceView has no setTransform(). It needs scaling or translating the view itself, or a crop.
Long-press screenshot and the debug-page snapshot of the single view now use PixelCopy, which needs API 24+. It also must not block the UI thread.
The switch from MJPEG to H.264/H.265 in single view hides and shows the view to get a fresh surface. This is untested.
Next: a real PR with the single view on a SurfaceView that covers these three points.
Activity
compot-dev commentedon Oct 13, 2026
Seen a third time, 2026-10-13 14:13, Aceline-1 (build 0242d14), an otherwise idle box (Wi‑Fi off, no other apps in use), while switching slots 2 and 3 to UDP through the URL dialog: 3×3 grid (8 cells + U, all SurfaceView) → tap slot 2 (grid stopped, single view with its TextureView) → Settings dialog → UDP → Save → menu → tap 3.
OpenGLRenderer: Davey! duration=48624ms … QueueBufferDuration=48550217000— the RenderThread waited 48.5 s inqueueBuffer, i.e. SurfaceFlinger did not take the window's buffer;Choreographer: Skipped 2914 frames.am_anr: Input dispatching timed out … Wait queue head age: 42159.8 ms; load average 8.8 (1 min) — lower than the earlier cases, so an overloaded box is not required.maininTextureView.onFrameAvailable.Common to all three cases: the window's layer set changes a lot at once (the grid of up to 9 SurfaceViews torn down, the TextureView of the single view and a dialog or popup appearing). Next step (planned today after #113): reproduce with a script (grid ↔ single ↔ dialog in a loop) and compare with fewer SurfaceViews / hiding the grid before removing it.
fix: set up one decoder at a time, 250 ms apart — nine at once stalle…
Revert "fix: a stopped grid cell closes its decoder in surfaceDestroy…
compot-dev commentedon Oct 13, 2026
Status 2026-10-13 15:35 — reproducible, cause not found yet (time-boxed).
Repro script on Aceline-1 (idle box, Wi‑Fi off): menu → slot 4 (grid → single), 4 s, menu → Q (single → grid), 8 s; repeat. Every run of 8–10 cycles gives 2–5
Davey!stalls of 3–68 s (QueueBufferDuration= the whole stall, i.e. SurfaceFlinger does not take the window's buffer) and usually an ANR; the longest ones come on single → grid. Oncemedia.codechung inDstate (load 74) and only a reboot helped.Ruled out (PR #127, closed):
surfaceDestroyed) — no change;Next ideas (when this is picked up again):
dumpsys SurfaceFlinger(layer count, HWC composition type),dumpsys SurfaceFlinger --latency,top -Hof surfaceflinger / composer /media.codec.removeAllViews+ rebuild) and only stop/start the sessions — fewer layer creations/destructions per switch.compot-dev commentedon Oct 13, 2026
One more data point (2026-10-13 16:55, Aceline-1, 6 cycles grid ↔ single): stalls of 24.5 s and 20.9 s again, but
dumpsys SurfaceFlingeranswered every ~4 s throughout them — SurfaceFlinger itself is not blocked. The window's ownqueueBufferwaits, i.e. the app's BufferQueue to SurfaceFlinger (a buffer not released back / a fence not signalled) rather than a frozen compositor. Next capture:dumpsys SurfaceFlinger --latency <app layer>and the app window's BufferQueue state during a stall, plus a run with the grid hidden (not removed) on single view.compot-dev commentedon Oct 13, 2026
Status 2026-10-13 evening (Aceline-1, idle box) — still reproducible, two more ideas ruled out.
Repro (script: grid → slot 4 single, 4 s → Q grid, 8 s; 8 cycles), stalls (
Davey!=QueueBufferDuration) and ANRs:removeAllViews+ rebuild (experiment)So neither creating/destroying the 9 SurfaceView layers nor the decode/update rate is the cause.
Captured during a stall (
dumpsys SurfaceFlinger, grid on screen): 8 of the 9 SurfaceView layers are composed by the GPU (CLIENT), one goes to an HWC overlay (DEVICE); the 1920×1080 / 2560×1920 YUV sources are scaled to 426×240 by Mali-G31 composition. surfaceflinger ~42 % CPU, load 6.9, 216 MB RAM free. The app window's own buffer still waits inqueueBufferfor tens of seconds.Next ideas (not tried):
Time-boxed here; back to deferred.
4 remaining items
compot-dev commentedon Oct 14, 2026
Status 2026-10-14: not reproducible right after a reboot.
Aceline-1 (.144), v2.01, rebooted at ~15:45 MSK (it had been up ~21 h; at that point cells 6–8 were stuck at Paused: decoder overloaded even on key frames). Same repro as yesterday: grid → slot 4 single (4 s) → Q grid (8 s), 8 cycles, driven by D-pad. Every cycle rebuilt the grid (checked in logcat).
Yesterday the same script gave stalls of 40–65 s on every run. Those runs came after long test sessions, on the same day as the decoder hang (#145).
New hypothesis: the stalls come from the box degrading with uptime or use (GPU/codec driver memory), not from the grid switch itself. Running now: a soak test on .144 (v2.02) that keeps the 3×3 grid playing and, every 40 min, logs
/proc/meminfo, the app's graphics memory and an 8-cycle repro. This will show whether the stalls come back as uptime grows.compot-dev commentedon Oct 14, 2026
Status 2026-10-14 evening: the stalls build up inside the app process; restarting the app clears them.
Aceline-1 (.144), v2.02, same repro (8 cycles of grid → slot 4 single → grid, D-pad):
So whatever builds up belongs to the app process, not to the box: a restart of the app alone brings it back to the clean state.
What does not grow with the cycles:
dumpsys media.resource_manager: 9 codecs for the live process; dead PIDs are listed with no clients);Not visible without root: the Mali driver's per-process GPU memory, and the app window's BufferQueue (the stall is the window's own
queueBuffer).Next (another day):
Davey!durations in one long run after an app restart.compot-dev commentedon Oct 15, 2026
Status 2026-10-15: a SurfaceView for the single view removes the stalls.
Aceline-1 (.144), box up ~30 min, same 3×3 layout. Each run starts from a fresh app process and does 60 cycles of grid → slot 4 single (4 s) → grid (8 s), driven by D-pad.
Davey!lines of the app process are counted per cycle.Davey!experiment/surfaceview-76), single view on a SurfaceViewWith the TextureView the stalls grow with the cycles in one process, as seen yesterday: up to 14 s within 60 cycles. With the SurfaceView there are almost no stalls and no growth. So the build-up is in the TextureView path, where frames go through the app's own GL composition and window buffer.
The experiment is not ready to merge. It loses three things that need a proper replacement:
setTransform(). It needs scaling or translating the view itself, or a crop.PixelCopy, which needs API 24+. It also must not block the UI thread.Next: a real PR with the single view on a SurfaceView that covers these three points.
Merge pull request #185 from OpenIPC/fix/surfaceview-76
feat: debug HTTP server on port 8085, LAN clients only (#96)
docs: fresh screenshots (single view with the transport line, debug p…
fix: single view on a SurfaceView instead of a TextureView (#76)
Merge pull request #185 from OpenIPC/fix/surfaceview-76
docs: README trimmed, details in docs/ (#191)