SIGN IN SIGN UP

fps: Add per-process GPU usage to the HUD (#2751)

## Summary

The HUD reported CPU and memory but nothing about the GPU, which for a
renderer is the half that usually explains a slow frame. This adds a
`GPU` reading for **this process' own share**, and colors the `FRAME`
value against the frame budget.

```
┌──────────────────────────┐
│  ﹋﹏  118 FPS  ﹋︿﹏﹋   │
│ FRAME             8.4 ms │  ← now colored against the budget
│ DROP                0.0% │
│ GPU                31.0% │  ← new
│ CPU 12.4%      MEM 84 MB │
└──────────────────────────┘
```

### Why per-process, not device-wide

A device-wide number moves for the compositor and every other window,
none of which the application can act on. It also would not sit sensibly
next to the per-process CPU beside it.

Each platform is read where it already attributes GPU time per process.
No vendor SDK, no elevated privilege:

| Platform | Source | Same counter as |
|---|---|---|
| macOS | `accumulatedGPUTime` on this process' accelerator clients in
the IO registry | Activity Monitor's GPU column |
| Windows | `\GPU Engine(*)\Utilization Percentage`, filtered to
`pid_<self>_` instances | Task Manager's GPU column |
| Linux | `drm-engine-*` in `/proc/self/fdinfo` | `nvtop`,
`intel_gpu_top` |

Two dead ends are recorded in `gpu/macos.rs` so they are not retried:
the accelerator's own `PerformanceStatistics` is easier to read but
device wide, and `task_info`'s `task_gpu_utilisation` is per process but
sits at zero on Apple silicon.

Where several engines run at once (Windows, Linux) the reading is the
busiest engine *type* rather than their sum, which would pass 100% while
the GPU still had headroom.

**The row is left out entirely where no per-process counter is
reachable**, rather than reading a flat zero — the web, an Intel Mac
(accelerator clients publish no `AppUsage`), and a Linux driver such as
nvidia's, which keeps its accounting in NVML.

### `FRAME` color

The headline is graded on the frame *rate*, so an idle window drawing a
handful of frames a second turns it red even though every one of those
frames landed well inside the budget. `FRAME` is graded on the frame
*time* with the same scale as the trace, so it staying green is what
tells you the application is fine and simply has nothing to redraw.

## Test plan

- Verified on macOS against a real load: the reading tracked the window
from 0% in the background, to 13% in the foreground, to 33% with the
example's curve count dialed up, cross-checked against `ioreg`'s
accumulated total for the same pid.
- `cargo clippy -p gpui-fps --all-targets -- -D warnings` clean; 12
tests pass; `rustfmt`, `typos` and `cargo machete` clean.
- The Windows and Linux backends cannot link the full `gpui` from macOS,
so both were extracted into an isolated crate and run through `cargo
clippy -D warnings` for `x86_64-pc-windows-msvc` and
`x86_64-unknown-linux-gnu`. The Linux `fdinfo` parser's tests (including
that `drm-engine-capacity-*` counts engines, not nanoseconds) pass on
the host. **Runtime behavior on those two platforms still needs
confirmation from CI or a reviewer on that hardware.**
- `wasm32-unknown-unknown` and the whole workspace compile.

No public API change: the new reading rides on the existing
`show_resources` and `resource_interval` builders.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: Codex <codex@openai.com>
J
Jason Lee committed
7a9ac172e804ce89aebac644a02f09813dc9e793
Parent: 5b3e18d
Committed by GitHub <noreply@github.com> on 8/18/2026, 7:32:50 AM