Merge perf/first-frame-latency: driver proto v4 + first-frame/resize latency (P0-P2)

Brings the first-frame-latency branch (P0.1 transition tracing, P1.1/P1.2
Welcome-time display prep, P2 in-place resize; pf-driver-proto v3 -> v4 with
IOCTL_UPDATE_MODES) onto current main. The branch predates the W6.2/W7 splits,
so git's rename detection carried most of it into the moved crates
(pf-capture idd_push, pf-vdisplay manager/pf_vdisplay, pf-win-display,
pf-driver-proto, the driver workspace) and the punktfunk1.rs remainder was
re-homed by hand:

- native/handshake.rs: welcome/start trace marks + the Welcome-time display
  prep spawn (the prep thread BECOMES the stream thread; hand-off via a
  SyncSender<SessionContext>). negotiate() gains bringup/quit/stop and returns
  the PrepHandle.
- native.rs: bringup/resize_ms creation + the stop/quit flags hoisted BEFORE
  the handshake (the close watcher splits: flags pre-handshake, lifecycle
  events post-handshake where `hello` exists); punch_done stamp; the data
  plane adopts the prep thread's result or builds inline.
- native/stream.rs: SessionContext/SendStats carry the trace; send_loop
  finishes it on the first video packet; the resize path gains the in-place
  fast path (try_inplace_resize) with the full rebuild as fallback, restructured
  so both share the post-rebuild bookkeeping; prepare_display/PreparedDisplay/
  PrepHandle; build_pipeline(+retry) thread the stage marks.
- session_status/mgmt: ttff_ms + last_resize_ms per session (union with the
  lifecycle-events fields main added to the same spots).
- pf-capture: Capturer gains capture_target_id() + resize_output() defaults.
- pf-vdisplay manager: perf's faster activation poll (60x50ms) + the settle
  floor before the PnP sweep, on main's knobs/no-trait shape.

Also: packaging/windows/build-gamepad-drivers.ps1 is ASCII again (an em-dash
from the pf-mouse work tripped windows-host.yml's locale-safety gate on main).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-17 16:08:16 +02:00
19 changed files with 1544 additions and 172 deletions
+1 -1
View File
@@ -14,7 +14,7 @@
--gamepad` consumes (per-driver .inf/.cat/.dll + one shared punktfunk-driver.cer).
Output (-Out): pf_dualsense.{dll,inf,cat} + pf_xusb.{dll,inf,cat} + pf_mouse.{dll,inf,cat} +
punktfunk-driver.cer. (pf_mouse is the resident virtual HID pointer, not a gamepad it shares
punktfunk-driver.cer. (pf_mouse is the resident virtual HID pointer, not a gamepad - it shares
this pipeline + the --gamepad install path.)
.EXAMPLE
@@ -129,6 +129,16 @@ pub unsafe extern "C" fn parse_monitor_description2(
return STATUS_NOT_FOUND;
};
let count = crate::monitor::flatten(&modes).count() as u32;
// Bring-up/diagnostic visibility (P2): does the OS ever RE-parse the description after an
// UPDATE_MODES? The head mode names which list generation this call served.
if let Some(head) = crate::monitor::flatten(&modes).next() {
dbglog!(
"[pf-vd] parse_monitor_description2(id={id}): {count} modes, head {}x{}@{}",
head.width,
head.height,
head.refresh_rate
);
}
out_args.MonitorModeBufferOutputCount = count;
if in_args.MonitorModeBufferInputCount < count {
// A zero input count is a count-only probe (success); a non-zero too-small buffer is an error.
@@ -204,6 +214,17 @@ pub unsafe extern "C" fn monitor_query_modes2(
return STATUS_NOT_FOUND;
};
let count = crate::monitor::flatten(&modes).count() as u32;
// Diagnostic visibility (P2): shows whether/when the OS re-queries target modes after an
// UPDATE_MODES (the head mode names the list generation this call served).
if let Some(head) = crate::monitor::flatten(&modes).next() {
dbglog!(
"[pf-vd] monitor_query_modes2: {count} modes, head {}x{}@{} (fill={})",
head.width,
head.height,
head.refresh_rate,
in_args.TargetModeBufferInputCount >= count
);
}
out_args.TargetModeBufferOutputCount = count;
if in_args.TargetModeBufferInputCount >= count {
// SAFETY: `pTargetModes` points to >= `count` IDDCX_TARGET_MODE2 entries.
@@ -95,6 +95,8 @@ pub unsafe fn dispatch(request: WDFREQUEST, ioctl_code: u32) {
control::IOCTL_SET_RENDER_ADAPTER => unsafe { set_render_adapter(request) },
// SAFETY: `request` is the framework WDFREQUEST.
control::IOCTL_SET_FRAME_CHANNEL => unsafe { set_frame_channel(request) },
// SAFETY: `request` is the framework WDFREQUEST.
control::IOCTL_UPDATE_MODES => unsafe { update_modes(request) },
_ => complete(request, STATUS_NOT_FOUND),
}
}
@@ -198,6 +200,28 @@ unsafe fn set_frame_channel(request: WDFREQUEST) {
}
}
/// `IOCTL_UPDATE_MODES` (v4): refresh a LIVE monitor's target-mode list to a new preferred mode —
/// the in-place mid-stream resize (`design/first-frame-and-resize-latency.md` P2). The monitor is
/// NOT departed: its OS identity, swap-chain machinery and retained frame stash all survive; the
/// host force-sets the freshly-advertised mode afterwards.
///
/// # Safety
/// `request` is the framework `WDFREQUEST`.
unsafe fn update_modes(request: WDFREQUEST) {
// SAFETY: `request` is the framework WDFREQUEST.
let Some(req) = (unsafe { read_input::<control::UpdateModesRequest>(request) }) else {
complete(request, STATUS_INVALID_PARAMETER);
return;
};
if !valid_mode(req.width, req.height, req.refresh_hz) {
complete(request, STATUS_INVALID_PARAMETER);
return;
}
let st =
crate::monitor::update_monitor_modes(req.session_id, req.width, req.height, req.refresh_hz);
complete(request, st);
}
/// `IOCTL_REMOVE`: depart + drop the monitor for the given session id.
///
/// # Safety
@@ -7,7 +7,7 @@
use std::sync::Mutex;
use std::time::{Duration, Instant};
use wdk_sys::{WDFOBJECT, call_unsafe_wdf_function_binding, iddcx};
use wdk_sys::{NTSTATUS, WDFOBJECT, call_unsafe_wdf_function_binding, iddcx};
/// One resolution with the refresh rates it supports.
#[derive(Clone)]
@@ -146,6 +146,41 @@ pub fn reap_orphaned(grace: Duration) -> usize {
n
}
/// Append `from`'s modes to `into`, skipping resolutions already present, capped at
/// [`MODE_LIST_CAP`] — the accumulate half of the union semantics (see [`update_monitor_modes`]).
fn union_modes(into: &mut Vec<Mode>, from: &[Mode]) {
for m in from {
if into.len() >= MODE_LIST_CAP {
break;
}
if !into
.iter()
.any(|e| (e.width, e.height) == (m.width, m.height))
{
into.push(m.clone());
}
}
}
/// The last advertised mode list of a DEPARTED monitor, per monitor id — consumed by the next
/// same-id [`create_monitor`] so a re-arrived monitor's ARRIVAL list already contains every mode
/// its predecessor ever served. The OS pins a monitor's settable set at arrival (see
/// [`update_monitor_modes`]), so this is what makes a windowed↔fullscreen cycle (or any return to
/// a previously-used size) an IN-PLACE mode set instead of another hotplug. In-process only (a
/// WUDFHost restart forgets it — harmless, the next resizes re-teach it); bounded: ≤ 16 ids ×
/// [`MODE_LIST_CAP`] modes.
static MODE_HISTORY: Mutex<Vec<(u32, Vec<Mode>)>> = Mutex::new(Vec::new());
/// Record a departing monitor's advertised list for its id ([`MODE_HISTORY`]).
fn remember_modes(id: u32, modes: &[Mode]) {
let mut hist = MODE_HISTORY.lock().unwrap_or_else(|e| e.into_inner());
if let Some(slot) = hist.iter_mut().find(|(i, _)| *i == id) {
slot.1 = modes.to_vec();
} else {
hist.push((id, modes.to_vec()));
}
}
/// Fallback modes appended after the requested mode, so a topology change still has options.
fn default_modes() -> Vec<Mode> {
vec![
@@ -365,9 +400,7 @@ pub fn preserve_publisher(
/// swap-chain's render adapter matches the publisher's ([`FramePublisher::render_adapter`]) — same
/// pooled device, so its context + opened ring textures are still valid; on a mismatch the caller drops
/// it and waits for a fresh channel delivery instead. `None` until a worker has stashed one.
pub fn take_preserved_publisher(
target_id: u32,
) -> Option<crate::frame_transport::FramePublisher> {
pub fn take_preserved_publisher(target_id: u32) -> Option<crate::frame_transport::FramePublisher> {
if target_id == 0 {
return None;
}
@@ -496,6 +529,15 @@ pub fn create_monitor(
let id = {
let mut lock = lock_monitors();
let id = resolve_id(&lock, preferred_id);
// Same-id mode history (P2 union semantics): a RE-ARRIVED monitor advertises every mode
// its departed predecessor served, so the OS's arrival-pinned settable set already
// contains them — a return to any previously-used size is then an IN-PLACE mode set.
{
let hist = MODE_HISTORY.lock().unwrap_or_else(|e| e.into_inner());
if let Some((_, prev)) = hist.iter().find(|(i, _)| *i == id) {
union_modes(&mut modes, prev);
}
}
lock.push(MonitorObject {
object: None,
id,
@@ -600,6 +642,77 @@ pub fn create_monitor(
Some((id, target_id, luid_low, luid_high))
}
/// How many distinct resolutions a monitor's advertised list may accumulate (the requested head +
/// history + the built-in fallbacks). Bounds the union growth across many resizes; the OLDEST
/// history entries fall off first.
const MODE_LIST_CAP: usize = 12;
/// `IOCTL_UPDATE_MODES` (v4): refresh the LIVE monitor's advertised mode list to lead with a new
/// preferred mode and push the new TARGET mode list to the OS via `IddCxMonitorUpdateModes2` —
/// the in-place mid-stream resize (`design/first-frame-and-resize-latency.md` P2). No departure:
/// the monitor's OS identity, its swap-chain worker and the retained frame stash all survive.
/// The `*2` (HDR) DDI matches the `*2` mode/buffer family this driver already requires
/// (IddCx 1.10), so it adds no new OS floor.
///
/// UNION semantics (on-glass finding, build 26200): the OS re-parses the description AND
/// re-queries target modes after `UpdateModes2` — our callbacks served the fresh list — yet the
/// SETTABLE set stays pruned to the modes known at monitor ARRIVAL (the monitor source-mode set
/// is pinned then). So replacing the list can only ever LOSE settable modes (v1 of this op
/// dropped the arrival mode from the target list, breaking even a resize BACK to it); the update
/// therefore accumulates — new mode first, every previously-advertised mode kept (deduped by
/// resolution, capped at [`MODE_LIST_CAP`]) — and the real payoff is at the NEXT re-arrival,
/// where [`create_monitor`]'s same-id history union makes every previously-used mode settable.
///
/// The stored list is updated FIRST (under the lock) so any OS re-query through the mode DDIs
/// ([`modes_for_object`]/[`modes_for_id`]) sees the new list, and REVERTED if the DDI fails — the
/// OS then still holds the old list and the two stay coherent. The DDI itself is called OUTSIDE
/// the lock (it may re-enter the mode-query callbacks, which lock [`MONITOR_MODES`]).
pub fn update_monitor_modes(session_id: u64, width: u32, height: u32, refresh: u32) -> NTSTATUS {
// Swap the stored list (union — see above) + grab the live handle under the lock.
let (object, old_modes, new_modes) = {
let mut lock = lock_monitors();
let Some(m) = lock.iter_mut().find(|m| m.session_id == session_id) else {
return crate::STATUS_NOT_FOUND;
};
let Some(object) = m.object else {
return crate::STATUS_NOT_FOUND; // created but not yet arrived — nothing to update
};
let mut new_modes = vec![Mode {
width,
height,
refresh_rates: vec![refresh],
}];
union_modes(&mut new_modes, &m.modes);
let old = core::mem::replace(&mut m.modes, new_modes.clone());
(object, old, new_modes)
};
// The OS's target-mode list for this monitor (the `*2`/HDR shape, like `monitor_query_modes2`).
let mut targets: Vec<iddcx::IDDCX_TARGET_MODE2> = flatten(&new_modes)
.map(|item| target_mode2(item.width, item.height, item.refresh_rate))
.collect();
let mut in_args = pod_init!(iddcx::IDARG_IN_UPDATEMODES2);
in_args.Reason = iddcx::IDDCX_UPDATE_REASON::IDDCX_UPDATE_REASON_OTHER;
in_args.TargetModeCount = targets.len() as u32;
in_args.pTargetModes = targets.as_mut_ptr();
// SAFETY: `object` is a live IddCx monitor handle (arrived — checked above; a concurrent REMOVE
// is serialized by the host, which only ever resizes a monitor its own session holds a lease
// on). `in_args` points at valid local storage (`targets` outlives the synchronous DDI call).
let st = unsafe { wdk_iddcx::IddCxMonitorUpdateModes2(object, &in_args) };
dbglog!(
"[pf-vd] IddCxMonitorUpdateModes2(session={session_id}, {width}x{height}@{refresh}) -> {st:#x}"
);
if !wdk_iddcx::nt_success(st) {
// Keep the stored list coherent with what the OS actually holds (the old one).
let mut lock = lock_monitors();
if let Some(m) = lock.iter_mut().find(|m| m.session_id == session_id) {
m.modes = old_modes;
}
return st;
}
crate::STATUS_SUCCESS
}
/// `IOCTL_REMOVE`: depart + drop the monitor for `session_id`. Returns true if one was removed.
pub fn remove_monitor(session_id: u64) -> bool {
// Pull out the IddCx handle AND the swap-chain processor under the lock, but drop the processor
@@ -611,6 +724,9 @@ pub fn remove_monitor(session_id: u64) -> bool {
return false;
};
let mut entry = lock.remove(pos);
// Keep the departing monitor's advertised list for its id — the next same-id create
// unions it back in (P2 mode history; see MODE_HISTORY).
remember_modes(entry.id, &entry.modes);
(entry.object, entry.swap_chain_processor.take())
};
// Drop the worker FIRST (it joins + deletes the swap-chain), THEN depart the monitor.
@@ -140,6 +140,16 @@ iddcx_ddi!(
in_args: *const iddcx::IDARG_IN_ADAPTERSETRENDERADAPTER,
) @ IddCxAdapterSetRenderAdapterTableIndex as PFN_IDDCXADAPTERSETRENDERADAPTER -> ()
);
iddcx_ddi!(
/// Refresh a LIVE monitor's target-mode list (the HDR `*2` variant, IddCx 1.10 — the same API
/// family as the `*2` mode/buffer DDIs this driver already requires): the OS re-evaluates which
/// modes the target supports WITHOUT a monitor departure, so the host can then mode-set to a
/// freshly-advertised mode in place (the mid-stream resize, latency plan P2).
IddCxMonitorUpdateModes2(
monitor: iddcx::IDDCX_MONITOR,
in_args: *const iddcx::IDARG_IN_UPDATEMODES2,
) @ IddCxMonitorUpdateModes2TableIndex as PFN_IDDCXMONITORUPDATEMODES2
);
iddcx_ddi!(
/// Bind a D3D device to an assigned swap-chain. HRESULT-shaped (0x887A0026 → retry on monitor flap).
IddCxSwapChainSetDevice(