Skip to content

fix: recover preview from WebGL context loss on Linux/Wayland#19

Open
EtienneLescot wants to merge 3 commits into
mainfrom
codex/fix-export-preview-webgl-context-loss
Open

fix: recover preview from WebGL context loss on Linux/Wayland#19
EtienneLescot wants to merge 3 commits into
mainfrom
codex/fix-export-preview-webgl-context-loss

Conversation

@EtienneLescot

@EtienneLescot EtienneLescot commented Jun 22, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Attaches a webglcontextlost listener to the preview Pixi canvas in VideoPlayback.tsx.
  • On loss, the listener calls event.preventDefault() (to opt in to Chromium's restoration attempt) and bumps a pixiGeneration state so the existing useEffect cleanup tears the broken app down. The next mount rebuilds it from scratch with a fresh WebGL context.
  • The wallpaper (rendered to a 2D canvas in frameRenderer.ts:212) survives the context loss, so this fix makes the "only background remains" symptom in issue [Bug]: Video disappears from editor after exporting (only background remains) #8 self-healing.
  • Extracted the handler into createWebGLContextLostHandler for unit testability and added VideoPlayback.test.ts with three focused tests (preventDefault is called, regenerate fires, generation is logged).
  • Logged both webglcontextlost and webglcontextrestored events so the recovery is visible in the terminal — combined with the console-message forwarding from PR [codex] Add export preview diagnostics #11, this gives a complete trace of any future loss/restoration on Linux.

Why

The bug only reproduces on Manjaro / CachyOS (Arch Linux + KDE Wayland, Mesa/EGL) and only after MP4 export — VideoEncoder uses the prefer-hardware path on Linux (videoExporter.ts:681), and the FrameRenderer also takes a Linux-specific CPU-readback branch (videoExporter.ts:397 and frameRenderer.ts:914).

grep webglcontextlost against the codebase before this PR returned 0 hits: the preview never registered a recovery listener, so once the GL context died the video sprite stayed gone. The wallpaper kept rendering because frameRenderer.ts:212 deliberately composites it on a plain 2D canvas outside of PixiJS, which is why the symptom is "wallpaper stays, video disappears" instead of a fully black preview.

Trade-offs

  • I deliberately did not force the encoder to prefer-software on Linux. Software H.264 is 3–10× slower on this workload and would have been a real UX regression for Manjaro users for a problem we had not yet confirmed. This fix keeps the encoder preferences untouched and makes the bug self-heal at the renderer instead.
  • I also did not change the encoder to fall back to software only after a context-loss event was seen — that would be a follow-up if the context-loss hypothesis turns out to be wrong, once PR [codex] Add export preview diagnostics #11's improved diagnostics confirm the root cause from cfinnberg's machine.

Test plan

  • npx tsc --noEmit — clean
  • npm run lint — clean
  • npm test — 244 passed (3 new in VideoPlayback.test.ts)
  • npm run build-vite — clean
  • Manual smoke (deferred): reproducing the bug requires real Manjaro KDE hardware, which I cannot do from a worktree. The handler is unit-tested for the two invariants the bug hinges on, and a real context-loss event will trigger the same code path the tests cover.

Follow-ups

  • Once PR [codex] Add export preview diagnostics #11 ships and we have a real trace from cfinnberg, confirm the WebGL context loss hypothesis and decide whether to also flip the Linux encoder preference to ["prefer-software", "prefer-hardware"] (the same order Windows uses at videoExporter.ts:679).
  • Optionally: add a webglcontextlost listener to the FrameRenderer's Pixi canvas in frameRenderer.ts as well. It only matters during export, where a lost context currently shows as a single green/empty frame, so it is much lower priority.

Related

Summary by CodeRabbit

Bug Fixes

  • Video preview now automatically recovers when WebGL context is lost, enhancing application stability and reliability while ensuring uninterrupted, seamless editing experience.

Tests

  • Added comprehensive test coverage for WebGL context loss handling and recovery, including validation of error management and diagnostic logging.

On Manjaro / CachyOS (Linux/Wayland with Mesa/EGL) the preview Pixi app
can lose its WebGL context during heavy GPU usage from the exporter —
most likely the `VideoEncoder`'s `prefer-hardware` path or the
Linux-only CPU readback in `frameRenderer.ts.readbackVideoCanvas`.
The wallpaper is rendered to a 2D canvas (`frameRenderer.ts:212`,
"Background renders separately, not in PixiJS") and survives the
context loss, but the video sprite lives inside the lost Pixi GL
context and never reappears. That is the "only background remains"
symptom in issue #8.

Fix: attach a `webglcontextlost` listener to the preview Pixi canvas.
`preventDefault()` opts in to Chromium's restoration attempt, and
bumping a `pixiGeneration` state tears the broken app down via the
existing `useEffect` cleanup so the next mount rebuilds it from
scratch with a fresh WebGL context. The recovery is logged for
diagnostics ("[VideoPlayback] WebGL context lost, recreating Pixi
app"), which will now reach the terminal thanks to the
console-message forwarding added to `electron/main.ts` in PR #11.

Extracted the handler into `createWebGLContextLostHandler` for unit
testability and added a small focused test that covers the two
invariants the bug hinges on: the event's `preventDefault` is called
and the regenerate callback fires.

Tested:
  - npx tsc --noEmit
  - npm run lint
  - npm test (244 passed, +3 new)
  - npm run build-vite
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@coderabbitai

coderabbitai Bot commented Jun 22, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: acd44d5a-d4bc-4b3c-88ac-d4d87c61e400

📥 Commits

Reviewing files that changed from the base of the PR and between 8246d07 and 7e73f52.

📒 Files selected for processing (2)
  • src/components/video-editor/VideoPlayback.test.ts
  • src/components/video-editor/VideoPlayback.tsx

📝 Walkthrough

Walkthrough

VideoPlayback.tsx gains WebGL context loss recovery: a new exported createWebGLContextLostHandler helper, a pixiGeneration state counter, webglcontextlost/webglcontextrestored listener registration on the Pixi canvas, a try/catch around app.init, and cleanup/dependency-array changes to trigger Pixi rebuild. A Vitest suite validates the new helper.

Changes

WebGL Context Loss Recovery

Layer / File(s) Summary
createWebGLContextLostHandler helper and pixiGeneration state
src/components/video-editor/VideoPlayback.tsx
Exports createWebGLContextLostHandler which calls event.preventDefault(), logs context loss with a captured generation value, and invokes a regenerate callback. Adds pixiGeneration React state to version Pixi canvas instances for teardown/rebuild tracking.
Pixi effect wiring: init guard, listener registration, cleanup, and dependency
src/components/video-editor/VideoPlayback.tsx
Declares handleContextLost/handleContextRestored variables for cleanup scope, wraps app.init in try/catch with early return on failure, attaches both context event listeners to app.canvas, extends cleanup to remove listeners before Pixi teardown, and changes the effect dependency array from [] to [pixiGeneration] so Pixi reinitializes after each context loss.
Vitest tests for createWebGLContextLostHandler
src/components/video-editor/VideoPlayback.test.ts
Adds three test cases verifying event.preventDefault() is called, the regenerate callback is invoked, and console.warn logs the construction-time generation value.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Poem

🐇 When WebGL goes dark and the canvas turns gray,
A handler hops in and saves the day!
preventDefault, log, then regenerate—
A new generation spawns, never too late.
With tests to confirm every bounce and call,
This rabbit-built fix will handle it all! 🎉

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately and concisely summarizes the main change: implementing recovery for WebGL context loss on Linux/Wayland systems that affects the video preview.
Description check ✅ Passed The description is comprehensive and well-structured with all key sections addressed including summary, related issue, type of change, release impact, desktop impact, testing, and follow-ups.
Linked Issues check ✅ Passed The PR fully addresses the requirement in issue #8 by implementing WebGL context loss recovery to restore video visibility in the preview after export on Linux/Wayland systems.
Out of Scope Changes check ✅ Passed All changes are directly scoped to fixing the WebGL context loss issue: test file for the handler, handler implementation in VideoPlayback, and listener registration within the Pixi initialization effect.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/fix-export-preview-webgl-context-loss

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

EtienneLescot added a commit that referenced this pull request Jun 24, 2026
- Add ROADMAP.md at repo root for top-level discoverability.
- Stability queue pulled from open issues / PRs on getopenscreen/openscreen (#8, #21, #22, #19, #18, #24).
- AI direction framed as opt-in / off by default, with per-feature tags.
- Extend .github/workflows/discord.yaml with a roadmap-notify job that posts an embed to the #🗺️・roadmap Discord channel whenever a PR changes ROADMAP.md (or docs/roadmap.md). Mirrors the existing PR -> #pr-reviews sync pattern. Activated by setting the DISCORD_ROADMAP_WEBHOOK_URL repo secret; silently skipped otherwise so CI never breaks.
EtienneLescot added a commit that referenced this pull request Jun 24, 2026
- Add ROADMAP.md at repo root for top-level discoverability.
- Stability queue pulled from open issues / PRs on getopenscreen/openscreen (#8, #21, #22, #19, #18, #24).
- AI direction framed as opt-in / off by default, with per-feature tags.
- Link the new roadmap from the README Community section.
- Extend .github/workflows/discord.yaml with a roadmap-notify job that posts an embed to the #🗺️・roadmap Discord channel whenever a PR changes ROADMAP.md (or docs/roadmap.md). Mirrors the existing PR -> #pr-reviews sync pattern. Activated by setting the DISCORD_ROADMAP_WEBHOOK_URL repo secret; silently skipped otherwise so CI never breaks.
EtienneLescot added a commit that referenced this pull request Jun 24, 2026
- Add ROADMAP.md at repo root for top-level discoverability.
- Stability queue pulled from open issues / PRs on getopenscreen/openscreen (#8, #21, #22, #19, #18, #24).
- AI direction framed as opt-in / off by default, with per-feature tags.
- Link the new roadmap from the README Community section.
- Extend .github/workflows/discord.yaml with a roadmap-notify job that posts an embed to the #🗺️・roadmap Discord channel whenever a PR changes ROADMAP.md (or docs/roadmap.md). Mirrors the existing PR -> #pr-reviews sync pattern. Activated by setting the DISCORD_ROADMAP_WEBHOOK_URL repo secret; silently skipped otherwise so CI never breaks.
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.

[Bug]: Video disappears from editor after exporting (only background remains)

1 participant