Skip to content

perf: WebSocket terminal transport, X11 backend, release prep v0.1.0 - #2

Merged
omartelo merged 3 commits into
mainfrom
perf/ws-transport
Jul 12, 2026
Merged

omartelo merged 3 commits into
mainfrom
perf/ws-transport

Conversation

@omartelo

Copy link
Copy Markdown
Collaborator

Summary

Final leg of the terminal performance campaign, plus the v0.1.0 release prep.

  • Local binary WebSocket transport for terminal I/O (b7f21ba): every keystroke used to be one Wails HTTP call through WebKit's NetworkProcess, and every output chunk one evaluate_javascript with base64 (~60 engine crossings/s under load). Terminal input and output now flow over a loopback WebSocket (random port, token-authenticated, binary [idLen][sessionId][payload] frames multiplexing all sessions), with automatic fallback to the Wails paths if the socket drops.
  • Force GDK_BACKEND=x11 on Linux (3e6fa25): root cause of the typing jank was Wayland fractional scaling — WebKitGTK renders every damage frame at 2x and downsamples on the CPU (~40ms/frame full-window, immune to raster backend). Under Xwayland the same workload is stall-free at 80+fps. Respects an explicit GDK_BACKEND override.
  • Release prep (be7bec7): CHANGELOG entries for the recent perf work, [Unreleased] moved under v0.1.0, compare links refreshed. build/config.yml was already at 0.1.0.

Measured end state (dev-validated on real hardware): typing spam full-window = 0 stalls; idle with 20+ sessions ≈ 0% CPU; scrollback reading ≈ 0 paint; nvim full-window scroll ~25-30fps (accepted ceiling — every line legitimately dirty).

Test plan

  • go test -race ./... (66 tests), go vet, gofmt clean
  • cd frontend && pnpm build && pnpm test (193 tests)
  • Manual A/B on Hyprland (Wayland fractional scaling): WS transport + X11 validated with [term-perf] logs

omartelo added 3 commits July 10, 2026 18:15
Every keystroke was an HTTP fetch through the WebKit network process and
every output chunk an evaluate_javascript call with a base64 payload —
~60 engine crossings/s while typing, measured as ~40ms main-thread stall
trains with every JS callback innocent (rAF instrumentation added to
term-perf).

One loopback WebSocket (random port + token) now multiplexes all
sessions' terminal I/O as binary frames [idLen][sessionId][payload] in
both directions: input frames land in the same PTY write as the Wails
binding, and the coalescer sends output frames directly, skipping
base64. When the socket is down the backend and frontend fall back to
the existing fetch/event paths automatically.

coder/websocket was already in the module graph via Wails; promoted to
a direct dependency.
…aints

WebKitGTK under Wayland fractional scaling renders every damage frame at
2x and downsamples on the CPU: typing in a full-size window cost ~40ms of
engine time per frame, saturating the main thread (~25 stalls/s) and
queueing input for seconds. GPU policy, DMABUF renderer, Skia painting
threads and canvas alpha were all A/B tested with identical results — the
cost is scale-conversion, not raster. Under X11/Xwayland the app sees an
integer scale: same spam workload runs stall-free at 80+fps full-window.

GDK_BACKEND is only set when absent so it stays overridable.
@omartelo
omartelo merged commit 74bb6d3 into main Jul 12, 2026
1 check passed
@omartelo
omartelo deleted the perf/ws-transport branch July 12, 2026 20:44
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