Recover from Vulkan fence/acquire timeout + device-lost
closed by a73x
Labels: backlog
[claude 2026-04-18] Diagnosed during today's debugging of a terminal freeze reported by the user.
## Observed symptom
Running waystty as a daily driver, window became visible-but-unresponsive: no keystrokes accepted, resize events ignored, last frame still on screen. Process (PID 2268004) had been up 11h52m, 74,470 voluntary context switches.
## Root cause
Main thread wedged in `vkWaitForFences(..., timeout=UINT64_MAX)` inside the NVIDIA 595.58.03 driver. Full backtrace:
```
main.runTerminal
renderer.Context.drawCells
vk.DeviceWrapper.waitForFences(..., timeout=18446744073709551615)
libnvidia-glcore internal poll() ← wedged here
```
The NVIDIA driver dropped a fence-signal for `in_flight_fence`. GPU work likely completed, but the kernel syncobj / sync fd the driver's userspace was polling never got notified. `vkWaitForFences` has no timeout, so the main loop is permanently blocked — no input dispatch, no resize handling, no exit.
Not a waystty logic bug in itself — a Vulkan-tutorial antipattern (`UINT64_MAX`) meeting a real-world driver flake.
## Offending sites (all use `std.math.maxInt(u64)`)
- `src/renderer.zig:1275` — `drawClear` fence wait
- `src/renderer.zig:1282` — `drawClear` acquireNextImageKHR
- `src/renderer.zig:1478` — atlas transfer fence wait
- `src/renderer.zig:1710` — `drawCells` fence wait (the one wedged today)
- `src/renderer.zig:1721` — `drawCells` acquireNextImageKHR
- `src/renderer.zig:1818` — capture path fence wait
- `src/renderer.zig:1824` — capture fence wait
## Proposed scope
Full device-lost recovery:
1. **Bounded timeouts** on every `waitForFences` and `acquireNextImageKHR` call. Suggested: 2s for fence waits, 100ms for acquireNextImage.
2. **Classify errors**: `VK_TIMEOUT`, `VK_ERROR_OUT_OF_DATE_KHR`, `VK_ERROR_DEVICE_LOST`.
3. **On timeout** (first occurrence): log, skip the frame, retry on the next iteration. Don't spam.
4. **On repeated timeout (N consecutive) or `VK_ERROR_DEVICE_LOST`**: tear down and recreate:
- Swapchain (reuse current code path)
- All fences (`in_flight_fence`, `atlas_transfer_fence`, `capture_fence`)
- Pipeline + render pass (if device-lost)
- Atlas GPU image + instance buffer (CPU-side glyph cache survives; reupload on demand)
- Possibly the `VkDevice` itself on true device-lost
5. **State preservation**: `vt.Terminal` grid lives in CPU memory and must not be touched by recovery. Font atlas CPU cache also survives — only GPU-side resources get rebuilt. After recovery, mark the terminal fully dirty and re-render from scratch.
6. **User-visible signal**: a one-line warning on stderr when recovery fires, so a repeated driver flake is observable instead of invisible.
## Non-goals (for v1)
- Exotic swapchain-lost paths (surface lost, full-screen exclusive flicker, etc.) — handle only the fence-timeout and `DEVICE_LOST` cases that match the real-world symptom.
- Persisting scrollback across a device-lost event — stays in RAM, survives naturally.
## Related
- #7b6053b0 — opt-in file logging for freeze/debug diagnosis. Complementary: logging helps diagnose *future* wedges, this ticket is about surviving them.
## Next step
Needs a design (brainstorm → spec → plan). The recovery path touches renderer ownership + lifecycle of every GPU resource; not a one-file change.
Close reason: [claude 2026-04-18] Duplicate of ab6c92f0 (older by ~1 minute, identical body). Closing this one; work tracked on ab6c92f0.