perf(host): in-place mid-stream resize — mode-set the live monitor, keep the capturer

Latency plan P2.2/P2.3: against a v4 driver the manager's resize branch now
runs UPDATE_MODES -> wait-mode-advertised (the OS re-enumerates async) ->
set_active_mode -> verified-state settle (P0.2) on the SAME monitor — no
REMOVE->ADD hotplug, no departure settle, no activation ladder, no re-isolate;
Windows keeps the per-monitor DPI (identity preserved). Any failure (v3
driver, mode never advertised, settle miss) falls back to the proven
re-arrival path unchanged.

On top of that the session's resize handler keeps the WHOLE capture pipeline:
the IDD-push capturer re-sizes its ring immediately (Capturer::resize_output —
no DescriptorPoller two-strike debounce, which stays for EXTERNAL changes),
the driver re-attaches and the mode-set full redraw provides the first frame;
only the encoder is swapped once the first new-size frame arrives
(open_video is ms-scale — P2.4 deliberately skipped). The capturer, send
thread and session transport all survive; every decline routes to the full
rebuild. Resize-trace stages (display_resized, ring_recreated,
first_new_frame, encoder_open) extend the P0.1 timeline.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-16 17:12:14 +02:00
parent 0899e53903
commit c2b9b32904
6 changed files with 487 additions and 78 deletions
@@ -258,6 +258,51 @@ pub(crate) unsafe fn wait_mode_settled(
}
}
/// Wait (bounded) until `gdi_name` ADVERTISES `mode`'s resolution in its display-mode list — the
/// gate between a driver-side mode-list refresh (`IOCTL_UPDATE_MODES`, latency plan P2) and the
/// CCD/GDI force-set: the OS re-evaluates an indirect display's settable modes asynchronously after
/// `IddCxMonitorUpdateModes2`, so an immediate `set_active_mode` could race the re-enumeration and
/// silently leave the old mode. Returns `true` once any refresh at the requested WxH is enumerable.
pub(crate) fn wait_mode_advertised(
gdi_name: &str,
mode: Mode,
ceiling: std::time::Duration,
) -> bool {
let wname: Vec<u16> = gdi_name.encode_utf16().chain(std::iter::once(0)).collect();
let deadline = std::time::Instant::now() + ceiling;
loop {
let mut i = 0u32;
loop {
let mut dm = DEVMODEW {
dmSize: size_of::<DEVMODEW>() as u16,
..Default::default()
};
// SAFETY: `wname` is a live NUL-terminated UTF-16 device name whose pointer stays valid
// for the call; `&mut dm` is a live, size-stamped DEVMODEW the API fills for mode index
// `i`. Both outlive this synchronous call.
let ok = unsafe {
EnumDisplaySettingsW(
PCWSTR(wname.as_ptr()),
ENUM_DISPLAY_SETTINGS_MODE(i),
&mut dm,
)
}
.as_bool();
if !ok {
break;
}
if dm.dmPelsWidth == mode.width && dm.dmPelsHeight == mode.height {
return true;
}
i += 1;
}
if std::time::Instant::now() >= deadline {
return false;
}
std::thread::sleep(std::time::Duration::from_millis(25));
}
}
/// Monitor-departure wait (latency plan P0.3): after a REMOVE, poll until the target has left the
/// ACTIVE CCD set — two consecutive absent samples, so one transient query failure mid-teardown
/// can't read as "gone" — instead of sleeping the fixed departure settle. `ceiling` (the old fixed