Skip to content

fix(linux): fall back to software rendering after repeated GPU crashes - #8

Open
pango07 wants to merge 2 commits into
masterfrom
fix/linux-gpu-crash-fallback
Open

fix(linux): fall back to software rendering after repeated GPU crashes#8
pango07 wants to merge 2 commits into
masterfrom
fix/linux-gpu-crash-fallback

Conversation

@pango07

@pango07 pango07 commented Aug 10, 2026

Copy link
Copy Markdown
Owner

Found while looking into #5. Unproven against real hardware — see the testing section before merging.

the bug

On some Linux setups (AMD integrated graphics on Mesa in the reported case) the GPU process fails to launch, Chromium retries, and then kills the app:

[ERROR:gpu_process_host.cc(976)] GPU process launch failed: error_code=1002
[WARNING:gpu_process_host.cc(1416)] The GPU process has crashed 1 time(s)
... (repeats 6 times) ...
[FATAL:gpu_data_manager_impl_private.cc(423)] GPU process isn't usable. Goodbye.

There is no fallback for this anywhere in src/main right now: no app.disableHardwareAcceleration(), no app.commandLine.appendSwitch. So the app is unusable on affected machines, and the only workaround is passing --disable-gpu by hand, which tends to break the transparent overlay windows rather than help.

the fix

You can't detect a GPU that will fail before it fails, so this counts failures across runs and degrades in tiers on the next launch:

tier trigger behavior
0 default hardware acceleration
1 3 failures disableHardwareAcceleration()
2 6 failures tier 1 + disable-gpu-sandbox

Tier 2 matters because disableHardwareAcceleration() does not stop the GPU process from launching — it only skips GL/driver init. That fixes a crash during Mesa driver setup, but not one where the sandbox fails to start the process at all, which is a commonly reported cause of this same error signature. If a machine keeps failing while already in software rendering, the sandbox is the next suspect. This tier is reasoned from the error class, not confirmed on hardware.

Counting rules that matter:

  • Only crashed, abnormal-exit, launch-failed, integrity-failure count. clean-exit and killed are normal teardown and an OOM kill — counting those would let ordinary shutdowns accumulate into a permanent downgrade on a healthy machine.
  • The counter resets only at tier 0, and only after a run that saw no GPU failure at all. Resetting while the fallback is active would clear the count because we degraded, then re-enable acceleration next launch and crash again forever.
  • Recovery from tier 1+ is the documented gpu-state.json deletion, printed in the log line.
  • FLICKY_DISABLE_GPU=1 or =2 forces a tier.

Runs pre-ready (the switches only apply before ready, and the failure being counted happens during startup), gated on the single-instance lock so a duplicate launch never touches the counter.

testing

npm run typecheck passes. npm run lint fails on master too, unrelated: eslint 9 wants a flat eslint.config.js and the repo doesn't have one.

Not runtime-tested against a failing GPU — I don't have that hardware. FLICKY_DISABLE_GPU=1 npm start exercises the fallback path on any machine, which is worth doing before merge.

the open question that decides whether this works at all

It is unverified whether Electron emits child-process-gone to JS before Chromium's LOG(FATAL) aborts the process. If GpuDataManagerImpl's observer runs first, the app dies before JS ever sees the events, the counter never increments, and the fallback never engages. The writes are synchronous with an fsync so each captured event has the best chance of landing, but that's mitigation, not proof.

One test on an affected machine settles it: launch, let it die, then check whether gpu-state.json in the app data directory has a non-zero count. If it's absent or zero, this approach doesn't work and the fallback needs to be driven by something other than crash events — likely a "did the last launch reach ready?" sentinel written at startup and cleared on clean shutdown.

also unresolved

Nothing gates the overlay windows in fallback mode. They're fullscreen, transparent: true, alwaysOnTop: 'screen-saver', one per display, fed cursor positions every 16ms. Electron on Linux has a history of transparent windows rendering opaque black without GPU compositing — a black fullscreen always-on-top overlay would be a worse outcome than the crash. Worth checking on the target config; if it's a problem, skipping overlay creation at tier 1+ is the obvious follow-up.

On some Linux setups (AMD integrated graphics on Mesa) the GPU process
fails to launch, Chromium retries, and then kills the app outright:

  [ERROR:gpu_process_host.cc] GPU process launch failed: error_code=1002
  [FATAL:gpu_data_manager_impl_private.cc] GPU process isn't usable. Goodbye.

There was no fallback for this anywhere in main, so the app was simply
unusable on affected machines and the only workaround was passing
--disable-gpu by hand, which breaks the transparent overlay windows.

Count GPU process crashes in userData and, once a machine looks
chronically affected, start the next launch with hardware acceleration
off. A run that stays up for a minute without a GPU crash clears the
counter, so a one-off crash mid driver update doesn't permanently
downgrade a healthy machine. FLICKY_DISABLE_GPU=1 forces the fallback
for anyone who wants to confirm the diagnosis immediately.

Refs #5
@vercel

vercel Bot commented Aug 10, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
flicky Ignored Ignored Preview Aug 10, 2026 7:30pm

Request Review

Review caught three defects in the first cut.

The reset timer cleared the counter while the fallback was active. A
broken machine would crash until the counter tripped, come up stable in
software rendering, reset itself at 60s because "no GPU crash this run",
and re-enable acceleration on the next launch — crashing forever on any
session longer than a minute. The reset now only runs at tier 0, and
only for a run that saw no GPU failure at all, so a recovered run no
longer wipes an accumulating count either.

Every child-process-gone event with type GPU was counted, including
clean-exit and killed. Normal teardown at quit, or an OOM kill, would
accumulate into a permanent downgrade on a healthy machine. Only
crashed, abnormal-exit, launch-failed and integrity-failure count now.

The listener was attached inside whenReady().then(), but the failure it
counts is a startup launch failure that begins before ready resolves —
the events it existed to catch were the ones it could miss. It now
attaches pre-ready, gated on the single-instance lock so a duplicate
launch still never writes.

Also: disableHardwareAcceleration does not stop the GPU process from
launching, so it can't fix a sandbox-level launch failure, which is a
commonly reported cause of this same error signature. Added a second
tier that appends disable-gpu-sandbox when a machine keeps failing while
already in software rendering. And clamp the persisted counter through
Number.isFinite — NaN is a number and compares false against every
threshold, which would have disabled the fallback outright.
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.

2 participants