perf(host): session-transition trace + Welcome-time display prep (native path)

Latency plan P0.1 + P1.1/P1.2 (design/first-frame-and-resize-latency.md):

P0.1 — every native session runs a bringup::Trace (hello -> welcome -> start
-> punch_done -> display_acquired -> capture_attached -> first_frame ->
encoder_open -> first_au -> first_packet), one summary info! line when the
first video packet leaves; each accepted resize runs its own trace
(reconfigure -> pipeline_rebuilt). Totals surface per session as
time_to_first_frame_ms / last_resize_ms in session_status -> mgmt /status,
so every subsequent latency change is measured, not vibed. (The Windows
manager logs its own activation/settle deltas — correlate by wall clock.)

P1.1/P1.2 — on the Windows native path the display bring-up no longer
serializes behind the Start round-trip and the up-to-2.5 s hole-punch wait:
a prep thread kicks off at Welcome (mode is final there) and runs monitor
create -> activation -> verified settle -> capture attach -> first frame ->
encoder open while the network waits are in flight; the data plane hands it
the post-punch SessionContext and it becomes the stream thread on a warm
pipeline. Abort between Welcome and Start drops the hand-off channel and the
prep result releases into the keep-alive machinery (stop/quit + watcher are
created pre-handshake so a vanished client also aborts the build retries).
Same slot-scoped begin_idd_setup serialization as the inline path. Linux
keeps the inline bring-up (launch semantics bind before create); GameStream
untouched.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-16 16:45:34 +02:00
parent e62cd5448e
commit 8374dfedf3
5 changed files with 451 additions and 74 deletions
+12
View File
@@ -391,6 +391,12 @@ struct StreamInfo {
/// Client's parity floor per FEC block (`minRequiredFecPackets`).
min_fec: u8,
codec: ApiCodec,
/// Session bring-up total, hello → first video packet, in ms (native sessions; `null` on the
/// GameStream plane or while the session is still bringing up).
time_to_first_frame_ms: Option<u32>,
/// Most recent mid-stream resize total, reconfigure → pipeline rebuilt, in ms (native sessions;
/// `null` when no resize happened / GameStream).
last_resize_ms: Option<u32>,
}
/// Non-sensitive host status for the local tray icon: counts and booleans only — no PIN values,
@@ -1480,6 +1486,9 @@ async fn get_status(State(st): State<Arc<MgmtState>>) -> Json<RuntimeStatus> {
packet_size: c.packet_size as u32,
min_fec: c.min_fec,
codec: c.codec.into(),
// Transition latencies are traced on the native plane only (latency plan P0.1).
time_to_first_frame_ms: None,
last_resize_ms: None,
})
.or_else(|| {
native.first().map(|s| StreamInfo {
@@ -1492,6 +1501,9 @@ async fn get_status(State(st): State<Arc<MgmtState>>) -> Json<RuntimeStatus> {
packet_size: 0,
min_fec: 0,
codec: s.codec.into(),
time_to_first_frame_ms: (s.time_to_first_frame_ms > 0)
.then_some(s.time_to_first_frame_ms),
last_resize_ms: (s.last_resize_ms > 0).then_some(s.last_resize_ms),
})
});
Json(RuntimeStatus {