Skip to content

fix(video-grid): scope forced bind to on-screen page to avoid EGL context exhaustion - #803

Open
sapta100ms wants to merge 2 commits into
release-v2from
fix/videogrid-egl-context-exhaustion
Open

fix(video-grid): scope forced bind to on-screen page to avoid EGL context exhaustion#803
sapta100ms wants to merge 2 commits into
release-v2from
fix/videogrid-egl-context-exhaustion

Conversation

@sapta100ms

Copy link
Copy Markdown
Contributor

Problem

Crash on the video grid: RuntimeException: android.opengl.GLException: Failed to create EGL context (from SurfaceViewRenderer.initHMSVideoView.addTrack). High volume for large-room customers on low/mid-end GPUs (e.g. Entri).

Cause

Each video tile allocates its own EGL context (a capped GPU resource — commonly ~32 on low-end GPUs). RoomKit 1.3.10 changed the grid observers to bind unconditionally (isForceUpdate = true). With offscreenPageLimit = 1, three pager pages are alive, so the off-screen pages started creating contexts too — ~3× the live contexts. In large rooms this crosses the device cap and the next eglCreateContext fails → crash. (Latent before 1.3.10; the change amplified it.)

isForceUpdate = true was added to fix black tiles after an SFU migration (fragment recreated, LiveData delivers before onResume, bind gated out). That only ever mattered for the on-screen page.

Fix

Scope the forced bind to the page actually on screen, so off-screen pages no longer hold contexts (they bind lazily on onResume when swiped to). Live contexts drop back to ~1 page's worth (pre-1.3.10 baseline); the migration fix is preserved for the visible page.

  • VideoGridFragment — track the selected page per pager via ViewPager2.OnPageChangeCallback.
  • VideoGridPageFragmentisForceUpdate = isSelectedPage() on both the speaker and screenshare observers.
  • VideoGridBaseFragment — always release a removed tile's renderer (drop the visibility gate) so scoping the bind can't orphan a live context.

Testing

  • :room-kit:compileDebugKotlin passes.
  • The GPU-cap crash reproduces only on genuinely low-cap devices, so verify on a low/mid-end handset in a multi-page room: swipe the grid + toggle screenshare, confirm no GLException and video still renders on the visible page.

Note

SFU migration is not enabled on Android yet; when it is, verify the visible grid page re-renders after a migration (the scoped force still covers it).

🤖 Generated with Claude Code

@sapta100ms
sapta100ms force-pushed the fix/videogrid-egl-context-exhaustion branch from 5335370 to a750552 Compare August 17, 2026 07:13
… context exhaustion

RoomKit 1.3.10 changed the video-grid speaker/track observers to always bind
(isForceUpdate=true), so off-screen pager pages (offscreenPageLimit=1 keeps 3
alive) also created a SurfaceViewRenderer + EGL context each. That roughly
tripled the live EGL contexts; in large rooms on low/mid-end GPUs (which cap
contexts, commonly ~32) the next tile's eglCreateContext fails and the app
crashes with 'Failed to create EGL context'.

Scope the forced bind to the page actually on screen (isSelectedPage()) instead
of forcing it on every live page. The visible page still force-binds, so the
SFU-migration recreate race the flag was added for is still handled; off-screen
pages bind lazily via onResume -> bindViews() when swiped to.

- VideoGridFragment: track the selected page per pager via OnPageChangeCallback.
- VideoGridPageFragment: isForceUpdate = isSelectedPage() on both observers.
- VideoGridBaseFragment: always release a removed tile's renderer (drop the
  visibility gate) so scoping the bind can't orphan a live EGL context.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@sapta100ms
sapta100ms force-pushed the fix/videogrid-egl-context-exhaustion branch from a750552 to 2076ff6 Compare August 17, 2026 07:40
…ilds

When the video-grid page count shrinks (e.g. SFU migration drops pages to 0
then rebuilds), ViewPager2 clamps currentItem to 0 but does not reliably
dispatch onPageSelected(0). That left currentPeerGridPage/currentScreenSharePage
stale, so the visible page-0 evaluated isSelectedPage()==false and skipped the
force-bind — reintroducing the transient black tile on the visible page after a
migration. Mirror the clamp: reset the tracker to 0 when the new page count
makes the tracked index out of range. A simple grow (e.g. 3->4 pages) does not
clamp, so the tracker is preserved.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant