bench: make wl-latency compositor-independent so the instrument can leave the felis tree #263

Open
opened 2026-09-12 17:24:59 +09:00 by natsukium · 0 comments
Owner

Problem

wl-latency is the Linux half of the latency suite and the only piece of the bench harness still tied to one
compositor. Three sites shell out to niri msg --json:

  • focused_window() (scripts/bench/wl-latency/src/main.rs:551) — the focused window's id and pid, behind
    require_focus (calibration, main.rs:1092, main.rs:1201) and require_focus_id (measurement, main.rs:839,
    re-checked before every keystroke at main.rs:912).
  • focused_output() (main.rs:770) — the focused output's logical width/height/scale, plus the refusal on a
    transformed output.

The harness around it is already pluggable: scripts/bench/wm/ selects a WindowPin per desktop (MacTilerPin,
NiriPin, FloatingPin) and suite_latency asks it for nothing but focus_for_typing(pid) → a window id
(suites.py:791). The Python side would accept a second compositor today; the instrument is what refuses.

This is the recorded revisit trigger firing (docs/explanation/testing.md:241): "Revisit if a second Linux compositor
enters the field, at which point wl-latency's niri-only focus check … needs a second implementation".

Why it matters beyond the field

Compositor-independence is also the precondition for moving the instrument out of this repo. It is already its own
crate outside the workspace with no felis dependency (scripts/bench/devshell.nix:86), and Typometer has been dead
since 2017 and will not come to Wayland — so a published, self-calibrating Wayland latency instrument has an empty
niche to fill. While it is niri-only, publishing it invites sway/Hyprland issues that a felis-internal tool never
receives. Independence first, extraction second (its own issue, once this lands).

Two tiers, one in scope

  • wlroots family (sway, Hyprland, …): both measurement protocols — zwp_virtual_keyboard_v1 and
    zwlr_screencopy_manager_v1 — are already there, so only the two niri IPC queries need replacing. This is the scope.
  • GNOME/KDE: neither protocol. Needs libei plus the portal's ScreenCast, which is a different instrument. Out of
    scope; the suite should skip with a reason rather than pretend.

The two queries

Output geometry — take it from wl_output/xdg_output. The tool is already a Wayland client, so logical size,
scale and transform arrive over the protocol instead of over any IPC: compositor-agnostic and strictly less code.
Uncontroversial.

Focus — no core protocol lets a client ask who holds the keyboard. Three candidates:

  1. zwlr_foreign_toplevel_manager_v1: its state event carries activated, the wlroots family implements it (the
    same family that provides screencopy), and wayland-protocols-wlr is already a dependency. The catch is identity —
    the suite passes a compositor window id, which means nothing here — so the handshake would have to change to
    something both sides can name: a unique title the launch sets (OSC 0/2) plus app_id, matched against the activated
    handle.
  2. A compositor-IPC trait inside the tool, mirroring wm/: niri msg / swaymsg -t get_tree / hyprctl activewindow -j. Keeps today's handshake, but multiplies exactly what this issue exists to remove, and every new
    compositor then needs a code change in the instrument.
  3. Move focus verification wholly into the caller and let the tool inject unconditionally. Rejected on its face: the
    per-keystroke re-check (main.rs:912) is why a window that loses focus mid-leg fails the leg instead of typing two
    hundred characters into the desktop.

(1) is the one to try first. Whichever lands, the decision belongs in docs/explanation/testing.md beside the existing
record.

Acceptance

  • No Command::new("niri") left in main.rs.
  • wl-latency --calibrate still clears its own gate on niri (p95 − p5 under 5 ms), with the numbers re-recorded:
    replacing the focus path changes what the instrument does between injection and detection, so the old figures do not
    carry over.
  • One leg measured on a second wlroots compositor (sway), calibration first. If the bars there are not comparable with
    niri's, that is a finding to record, not a blocker.
  • Tier-2 desktops skip with a reason, through the pin's focus_for_typing answering None — already FloatingPin's
    behavior (wm/floating.py:44).

Where

scripts/bench/wl-latency/src/main.rs; scripts/bench/wm/ (a sway pin, if the handshake changes);
scripts/bench/suites.py; docs/explanation/testing.md (the record and its Revisit if);
docs/reference/testing.md:383 ("wl-latency drives niri only"); .agents/skills/perf-trace/references/harnesses.md.

## Problem `wl-latency` is the Linux half of the `latency` suite and the only piece of the bench harness still tied to one compositor. Three sites shell out to `niri msg --json`: - `focused_window()` (`scripts/bench/wl-latency/src/main.rs:551`) — the focused window's id and pid, behind `require_focus` (calibration, `main.rs:1092`, `main.rs:1201`) and `require_focus_id` (measurement, `main.rs:839`, re-checked before every keystroke at `main.rs:912`). - `focused_output()` (`main.rs:770`) — the focused output's logical width/height/scale, plus the refusal on a transformed output. The harness around it is already pluggable: `scripts/bench/wm/` selects a `WindowPin` per desktop (`MacTilerPin`, `NiriPin`, `FloatingPin`) and `suite_latency` asks it for nothing but `focus_for_typing(pid)` → a window id (`suites.py:791`). The Python side would accept a second compositor today; the instrument is what refuses. This is the recorded revisit trigger firing (`docs/explanation/testing.md:241`): "_Revisit if_ a second Linux compositor enters the field, at which point `wl-latency`'s niri-only focus check … needs a second implementation". ## Why it matters beyond the field Compositor-independence is also the precondition for moving the instrument out of this repo. It is already its own crate outside the workspace with no felis dependency (`scripts/bench/devshell.nix:86`), and Typometer has been dead since 2017 and will not come to Wayland — so a published, self-calibrating Wayland latency instrument has an empty niche to fill. While it is niri-only, publishing it invites sway/Hyprland issues that a felis-internal tool never receives. Independence first, extraction second (its own issue, once this lands). ## Two tiers, one in scope - **wlroots family** (sway, Hyprland, …): both measurement protocols — `zwp_virtual_keyboard_v1` and `zwlr_screencopy_manager_v1` — are already there, so only the two niri IPC queries need replacing. This is the scope. - **GNOME/KDE**: neither protocol. Needs libei plus the portal's ScreenCast, which is a different instrument. Out of scope; the suite should skip with a reason rather than pretend. ## The two queries **Output geometry** — take it from `wl_output`/`xdg_output`. The tool is already a Wayland client, so logical size, scale and transform arrive over the protocol instead of over any IPC: compositor-agnostic and strictly less code. Uncontroversial. **Focus** — no core protocol lets a client ask who holds the keyboard. Three candidates: 1. `zwlr_foreign_toplevel_manager_v1`: its `state` event carries `activated`, the wlroots family implements it (the same family that provides screencopy), and `wayland-protocols-wlr` is already a dependency. The catch is identity — the suite passes a compositor window id, which means nothing here — so the handshake would have to change to something both sides can name: a unique title the launch sets (OSC 0/2) plus `app_id`, matched against the activated handle. 2. A compositor-IPC trait inside the tool, mirroring `wm/`: `niri msg` / `swaymsg -t get_tree` / `hyprctl activewindow -j`. Keeps today's handshake, but multiplies exactly what this issue exists to remove, and every new compositor then needs a code change in the instrument. 3. Move focus verification wholly into the caller and let the tool inject unconditionally. Rejected on its face: the per-keystroke re-check (`main.rs:912`) is why a window that loses focus mid-leg fails the leg instead of typing two hundred characters into the desktop. (1) is the one to try first. Whichever lands, the decision belongs in `docs/explanation/testing.md` beside the existing record. ## Acceptance - No `Command::new("niri")` left in `main.rs`. - `wl-latency --calibrate` still clears its own gate on niri (p95 − p5 under 5 ms), with the numbers re-recorded: replacing the focus path changes what the instrument does between injection and detection, so the old figures do not carry over. - One leg measured on a second wlroots compositor (sway), calibration first. If the bars there are not comparable with niri's, that is a finding to record, not a blocker. - Tier-2 desktops skip with a reason, through the pin's `focus_for_typing` answering `None` — already `FloatingPin`'s behavior (`wm/floating.py:44`). ## Where `scripts/bench/wl-latency/src/main.rs`; `scripts/bench/wm/` (a sway pin, if the handshake changes); `scripts/bench/suites.py`; `docs/explanation/testing.md` (the record and its _Revisit if_); `docs/reference/testing.md:383` ("`wl-latency` drives niri only"); `.agents/skills/perf-trace/references/harnesses.md`.
Sign in to join this conversation.
No description provided.