HUD and telemetry: report the game's fps and the displayed fps on both display servers

Closed
UT
7d ago
+88-2

Description

Two meters on both display servers

The HUD's FPS row and game_exited's avg_fps used to carry one number. This PR reports two quantities, the same way on X11 and gamescope:

  • Game fps: the frames the game produced.
  • Displayed fps: the frames that could reach the panel.

What "game fps" counts today (unchanged by this PR):

display serverwhat ticks FrameRating
X11One tick per content update of the tracked application window, i.e. per present the game sends to the app's X server (XServerScreen.onUpdateWindowContent). The renderer's per-frame listener is never enabled, and the Vulkan and SurfaceControl renderers do not implement it.
gamescopeOne tick per GameNativeDrm.onFramePublished. The daemon raises it once per frame the ICD shim reports as produced: acquired, plus generated under frame generation (gamenative-drm asc.rs note_produced). It falls back to one per frame published to the panel only while no shim report has arrived for a second (GameNative/gamenative-drm#42).

So with the shim reporting, both servers already count produced frames. A game rendering faster than the panel shows numbers the panel can never display.

Examples

caseHUD FPS row
Hades II uncapped, 60 Hz panelFPS 60 (287)
Capped at 60, 60 Hz panelFPS 59.8 (game and displayed are equal)
142 fps on a 120 Hz panelFPS 120 (142)

New telemetry fields

  • game_exited.game_fps: the same value as avg_fps, under an explicit name. avg_fps stays for continuity and is the game's fps.
  • game_exited.displayed_fps.
  • Session metadata: displayed_fps next to avg_fps.
  • The PowerMetrics log line gains displayed= next to fps=.

What is approximate

Displayed fps is an estimate: the game's one-second readings clamped to the panel's refresh rate. A frame the panel dropped below its rate is not seen. A measured count needs two things this app-only change does not add:

  • X11: a per-present counter in the Vulkan and SurfaceControl renderers.
  • gamescope: a separate publish upcall from the daemon. Today the app receives only the produced-frame upcall, because the publish path is muted while shim reports flow.

Other approximations:

  • Under gamescope with frame generation on, game fps includes the generated frames (as before).
  • On X11 with frame generation, the HUD keeps using the layer's measured output fps (as before).
  • The HUD's ceiling is PerformanceMetricsCollector.refreshRateHz, which is read at session start.

Behaviour

Nothing changes beyond the HUD text and the new telemetry fields. The tuner, the frame ring and the existing fields are untouched.

Recording

Type of Change

  • Bug fix
  • Performance / stability improvement
  • Compatibility improvements
  • Other (requires prior approval)

Checklist

  • If I have access to #code-changes, I have discussed this change there and it has been green-lighted. If I do not have access, I have still provided clear context in this PR. If I skip both, I accept that this change may face delays in review, may not be reviewed at all, or may be closed.
  • This change aligns with the current project scope (core functionality, stability, or performance). If not, it has been explicitly approved beforehand.
  • I have attached a recording of the change.
  • I have read and agree to the contribution guidelines in CONTRIBUTING.md.

Summary by cubic

Reports the game's fps and the displayed fps on both X11 and gamescope, so the HUD and telemetry no longer show numbers the panel cannot reach (e.g. an uncapped game at 287 fps on a 60 Hz panel).

The HUD FPS row now shows FPS 60 (287) when the game outruns the panel, and a single number when it does not. game_exited gains game_fps (same value as avg_fps) and displayed_fps; session metadata and the PowerMetrics log line carry the displayed figure too. The displayed fps is an estimate: the game's per-second readings clamped to the panel's refresh rate, since neither renderer reports the frames the panel actually latched. Existing fields, the tuner, and the frame ring are untouched.

Summary by CodeRabbit

  • New Features
    • The performance HUD now distinguishes the panel’s displayed frame rate from the game’s frame rate when the game runs faster than the panel refresh rate.
    • Performance metrics and session summaries now report displayed FPS separately from game FPS.