todo-app/docs/DOCS-desktop-performance-findings.md
Krzysztof kuhy Rudnicki 59ee407d80
Some checks failed
file-length / file length <= 250 lines (push) Has been cancelled
md-naming / markdown naming convention (push) Has been cancelled
release-apk / release (APK) (push) Has been cancelled
Adopt the four markdown namespaces and gate them
Renames this repo's markdown onto README.md / CLAUDE*.md / DOCS*.md /
TODO*.md so a future session can tell a live task from a finished
record without reading the file to find out.

TODO files carry the literal "REMOVE ME AFTER FINISH" and are deleted
when the work lands. Enforced by the md-naming pre-commit hook and CI
workflow, both delegating to the shared gate in kuhyx/utils so this
repo's idea of a valid name cannot drift from every other repo's.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A47aX5DJwmA3QFn3pkEWi7
2026-08-24 15:54:35 +02:00

74 lines
3.7 KiB
Markdown

# Linux desktop performance: measured findings
Investigation date: 2026-07-20. Machine: Arch, X11 + i3, NVIDIA proprietary
610.43.03, RTX 3090. DP-0 = 3840x2160 @144 Hz (primary), HDMI-0 = 2560x1440
@144 Hz. Flutter 3.44.6 stable.
## Conclusion
**The previous session was right: Flutter's Linux desktop embedder cannot drive
this machine's 4K display, and switching technology for the desktop app is
justified.**
Two independent defects, neither fixable in application code:
1. **Hard 60 FPS cap.** Flutter's Linux embedder schedules frames at a fixed
60 FPS regardless of the monitor (upstream
[#183703](https://github.com/flutter/flutter/issues/183703), P2, no PR).
2. **~20 FPS at 4K.** Far worse, and the actual cause of the reported lag. At
3840x2160 the app achieves 18.6 fps while each frame costs only ~3 ms of a
16.7 ms budget. The stock `flutter create` counter app behaves identically,
so this is the toolkit, not todo.
Application code is not implicated anywhere: build (Dart) time is 0.1-0.4 ms in
every configuration and at every window size measured.
An earlier revision of this document concluded that a rewrite was **not**
warranted. That conclusion was based only on defect 1 and is superseded by the
4K measurements below.
## Evidence
Measured with a temporary in-app instrument (`lib/frame_stats.dart`, armed by
`TODO_FRAME_STATS=1`) reporting frame-to-frame cadence alongside build/raster
times, driven by a perpetual animation so frames are produced continuously.
| Configuration | Cadence p50 | Achieved | build p50 | raster p50 |
|---|---|---|---|---|
| Release, Skia GL (default) | 16.67 ms | **60.0 fps** | 0.30 ms | 1.50 ms |
| Release, Impeller | 16.67 ms | **60.0 fps** | 0.19 ms | 1.33 ms |
| Release, Impeller + Vulkan | 16.67 ms | **60.0 fps** | 0.26 ms | 1.33 ms |
| Driver vsync off (`__GL_SYNC_TO_VBLANK=0`) | 16.67 ms | **60.0 fps** | 0.28 ms | 1.40 ms |
| picom compositor + Skia GL | 16.67 ms | **60.0 fps** | 0.27 ms | 1.62 ms |
| picom compositor + Impeller | 16.67 ms | **60.0 fps** | 0.30 ms | 1.44 ms |
| **Stock `flutter create` app** (no todo code) | 16.67 ms | **60.0 fps** | 0.12 ms | 1.35 ms |
| **Same Flutter code as a web build, in Chrome** | **7.00 ms** | **142.9 fps** | — | — |
> **Retracted:** an earlier revision of this table had a "real typing +
> scrolling" row. It was invalid. It used `xdotool type --window`, which sends
> synthetic `XSendEvent` keystrokes that GTK/Flutter ignore; the keystrokes
> actually landed in a different application. That run measured an idle app.
> Interaction is measured properly in the next section instead.
Confounds on the web row, stated for honesty: it ran on HDMI-0 (1440p@144) with
a game occupying the 4K GPU, which is why its p95 was 13.90 ms. The desktop
60.0 fps lock, by contrast, is clean, jitter-free and monitor-independent, so
the comparison's conclusion holds — but the web number should be re-confirmed on
the 4K monitor with the GPU idle.
Key readings:
- Cadence p50 **equals** p95 at exactly 16.67 ms in every desktop run — a hard
lock with zero jitter, not contention.
- Total per-frame work is ~1.8 ms against a 6.94 ms 144 Hz budget. The renderer
has **74% headroom at 144 Hz** and 89% at 60 Hz.
- Under real typing and scrolling, **one** frame exceeded the 144 Hz budget in
14 seconds and **zero** exceeded 60 Hz. There is no dropped-frame jank.
- The stock counter app behaves identically, so this is not `todo`'s code.
- Neither renderer choice, nor driver vsync, nor a compositor moves the number.
---
Part 2 — the interaction-lag half of this investigation, and the decision that came out of it — is in
[desktop-performance-interaction-lag.md](desktop-performance-interaction-lag.md).