perf(host+driver): in-place resize = advertised-mode fast path + mode-history union
On-glass round 2 settled the mechanism: after UpdateModes2 the OS re-parses our description AND re-queries target modes (driver log — both 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, below anything the driver can refresh. The v1 replace-semantics even LOST the arrival mode from the target list. Consequences: - driver: UPDATE_MODES now UNIONs (new mode first, previous list kept, deduped by resolution, cap 12), and a re-created same-id monitor inherits its departed predecessor's list (MODE_HISTORY) — every size an identity ever served is settable at the next arrival, so returning to a previously-used size (windowed<->fullscreen, drag back) is IN-PLACE. - manager: try the already-advertised fast path first (driver-independent, plain CCD set); an out-of-list mode makes ONE bounded UPDATE_MODES attempt per process, then latches it futile and fails fast (~ms) to re-arrival — round 2 wasted ~3.1 s per arbitrary resize on the doomed wait. Fallback log demoted warn->info (expected-normal for first-seen sizes). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -268,8 +268,15 @@ pub(crate) struct VirtualDisplayManager {
|
|||||||
device: Mutex<DeviceSlot>,
|
device: Mutex<DeviceSlot>,
|
||||||
watchdog_s: AtomicU32,
|
watchdog_s: AtomicU32,
|
||||||
/// The driver's handshake-reported protocol version (0 until the first open). The in-place
|
/// The driver's handshake-reported protocol version (0 until the first open). The in-place
|
||||||
/// resize (latency plan P2) gates on `>= 4`; a v3 driver keeps the re-arrival path.
|
/// resize (latency plan P2) gates its UPDATE_MODES attempt on `>= 4`; a v3 driver keeps the
|
||||||
|
/// already-advertised fast path + the re-arrival fallback.
|
||||||
driver_proto: AtomicU32,
|
driver_proto: AtomicU32,
|
||||||
|
/// Latched `true` after an UPDATE_MODES round-trip failed to make the new mode settable —
|
||||||
|
/// on-glass (build 26200) the OS pins a monitor's settable set at ARRIVAL (it re-parses our
|
||||||
|
/// description + re-queries target modes, then ignores both), so every further attempt for an
|
||||||
|
/// out-of-arrival-list mode would only waste ~1 s per resize before the same re-arrival
|
||||||
|
/// fallback. One attempt per process, in case a future OS build honors the refresh.
|
||||||
|
update_modes_futile: AtomicBool,
|
||||||
/// Monotonic lease-generation counter (was the `MON_GEN` global).
|
/// Monotonic lease-generation counter (was the `MON_GEN` global).
|
||||||
gen: AtomicU64,
|
gen: AtomicU64,
|
||||||
state: Mutex<MgrInner>,
|
state: Mutex<MgrInner>,
|
||||||
@@ -301,6 +308,7 @@ pub(crate) fn init(driver: Box<dyn VdisplayDriver>) -> &'static VirtualDisplayMa
|
|||||||
device: Mutex::new(DeviceSlot::default()),
|
device: Mutex::new(DeviceSlot::default()),
|
||||||
watchdog_s: AtomicU32::new(3),
|
watchdog_s: AtomicU32::new(3),
|
||||||
driver_proto: AtomicU32::new(0),
|
driver_proto: AtomicU32::new(0),
|
||||||
|
update_modes_futile: AtomicBool::new(false),
|
||||||
gen: AtomicU64::new(1),
|
gen: AtomicU64::new(1),
|
||||||
state: Mutex::new(MgrInner::default()),
|
state: Mutex::new(MgrInner::default()),
|
||||||
setup_lock: Mutex::new(()),
|
setup_lock: Mutex::new(()),
|
||||||
@@ -598,13 +606,14 @@ impl VirtualDisplayManager {
|
|||||||
_ => unreachable!("just matched Active"),
|
_ => unreachable!("just matched Active"),
|
||||||
};
|
};
|
||||||
if cur_mode != mode {
|
if cur_mode != mode {
|
||||||
// IN-PLACE mode set first (latency plan P2, driver protocol >= 4): refresh the
|
// IN-PLACE mode set first (latency plan P2): an already-advertised resolution
|
||||||
// live monitor's advertised modes (IOCTL_UPDATE_MODES) + CCD-force the new mode —
|
// (arrival list + the driver's same-id mode history) is CCD-forced on the SAME
|
||||||
// no REMOVE→ADD, so the monitor's OS identity (saved per-monitor DPI), the
|
// monitor — no REMOVE→ADD, so the monitor's OS identity (saved per-monitor DPI),
|
||||||
// driver-side swap-chain machinery and the retained frame stash all survive, and
|
// the driver-side swap-chain machinery and the retained frame stash all survive,
|
||||||
// the whole hotplug cost (departure settle + activation ladder + re-isolate)
|
// and the whole hotplug cost (departure settle + activation ladder + re-isolate)
|
||||||
// disappears. Any failure falls through to the proven re-arrival below.
|
// disappears. An out-of-list mode fails FAST (see `resize_in_place`) and falls
|
||||||
if self.driver_proto.load(Ordering::Relaxed) >= 4 {
|
// through to the proven re-arrival below.
|
||||||
|
{
|
||||||
let in_place = {
|
let in_place = {
|
||||||
let Some(SlotState::Active { mon, refs }) = inner.slots.get_mut(&slot)
|
let Some(SlotState::Active { mon, refs }) = inner.slots.get_mut(&slot)
|
||||||
else {
|
else {
|
||||||
@@ -629,10 +638,13 @@ impl VirtualDisplayManager {
|
|||||||
Some(out)
|
Some(out)
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
tracing::warn!(
|
// Expected-normal for a first-seen arbitrary size (the OS pins
|
||||||
|
// settable modes at arrival; the re-arrival teaches it) — info,
|
||||||
|
// not warn.
|
||||||
|
tracing::info!(
|
||||||
slot,
|
slot,
|
||||||
error = %format!("{e:#}"),
|
reason = %format!("{e:#}"),
|
||||||
"in-place resize failed — falling back to monitor re-arrival"
|
"in-place resize not possible — monitor re-arrival"
|
||||||
);
|
);
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
@@ -1177,6 +1189,36 @@ impl VirtualDisplayManager {
|
|||||||
.gdi_name
|
.gdi_name
|
||||||
.clone()
|
.clone()
|
||||||
.context("in-place resize needs a resolved GDI name")?;
|
.context("in-place resize needs a resolved GDI name")?;
|
||||||
|
let t0 = Instant::now();
|
||||||
|
// FAST PATH (driver-independent): the OS already offers this resolution — the monitor's
|
||||||
|
// arrival list, which since the driver's mode-history union contains every size this
|
||||||
|
// identity ever served — so a plain CCD mode set reaches it with no driver round-trip.
|
||||||
|
let already = crate::win_display::wait_mode_advertised(&gdi, mode, Duration::ZERO);
|
||||||
|
if !already {
|
||||||
|
// Out-of-arrival-list mode. On-glass (build 26200) the OS re-parses our description
|
||||||
|
// AND re-queries target modes after UpdateModes2 — our callbacks served the fresh
|
||||||
|
// list — yet the SETTABLE set stays pruned to the arrival list: the monitor
|
||||||
|
// source-mode set is pinned at arrival. So one bounded UPDATE_MODES attempt per
|
||||||
|
// process (in case a future build honors the refresh), then latch it futile and fail
|
||||||
|
// fast to the re-arrival — whose same-id history union makes THIS size settable in
|
||||||
|
// place from then on.
|
||||||
|
if self.driver_proto.load(Ordering::Relaxed) < 4 {
|
||||||
|
anyhow::bail!(
|
||||||
|
"{}x{} is not in the advertised mode set (v3 driver: in-place reaches only \
|
||||||
|
arrival-list modes)",
|
||||||
|
mode.width,
|
||||||
|
mode.height
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if self.update_modes_futile.load(Ordering::Relaxed) {
|
||||||
|
anyhow::bail!(
|
||||||
|
"{}x{} is not in the advertised mode set (UPDATE_MODES latched futile — the \
|
||||||
|
OS pins settable modes at monitor arrival; the re-arrival teaches this size \
|
||||||
|
to the identity's history)",
|
||||||
|
mode.width,
|
||||||
|
mode.height
|
||||||
|
);
|
||||||
|
}
|
||||||
tracing::info!(
|
tracing::info!(
|
||||||
old = format!(
|
old = format!(
|
||||||
"{}x{}@{}",
|
"{}x{}@{}",
|
||||||
@@ -1186,38 +1228,23 @@ impl VirtualDisplayManager {
|
|||||||
target = mon.target_id,
|
target = mon.target_id,
|
||||||
"virtual-display: updating the live monitor's modes for an in-place resize"
|
"virtual-display: updating the live monitor's modes for an in-place resize"
|
||||||
);
|
);
|
||||||
// SAFETY: `dev` is the live control handle (this fn's contract); `update_modes` forwards it
|
// SAFETY: `dev` is the live control handle (this fn's contract); `update_modes`
|
||||||
// to a synchronous IOCTL with owned/borrowed locals only.
|
// forwards it to a synchronous IOCTL with owned/borrowed locals only.
|
||||||
unsafe { self.driver.update_modes(dev, &mon.key, mode) }?;
|
unsafe { self.driver.update_modes(dev, &mon.key, mode) }?;
|
||||||
// The OS does NOT re-evaluate an indirect display's settable modes on its own after
|
|
||||||
// UpdateModes2 (on-glass: the new mode never became enumerable within 2 s) — force a mode
|
|
||||||
// re-enumeration by re-committing the current config (the same SDC_FORCE_MODE_ENUMERATION
|
|
||||||
// re-commit the isolate/layout paths use), then wait for the new resolution to appear,
|
|
||||||
// re-kicking a couple of times. Without it the CDS_TEST inside `set_active_mode` would
|
|
||||||
// reject the mode and silently keep the old one.
|
|
||||||
let t0 = Instant::now();
|
|
||||||
let mut advertised = false;
|
|
||||||
for kick in 0..3u32 {
|
|
||||||
// SAFETY: CCD query/apply FFI under the held `state` lock (this fn's contract).
|
// SAFETY: CCD query/apply FFI under the held `state` lock (this fn's contract).
|
||||||
unsafe { crate::win_display::force_mode_reenumeration() };
|
unsafe { crate::win_display::force_mode_reenumeration() };
|
||||||
if crate::win_display::wait_mode_advertised(&gdi, mode, Duration::from_millis(1000)) {
|
if !crate::win_display::wait_mode_advertised(&gdi, mode, Duration::from_millis(800)) {
|
||||||
advertised = true;
|
self.update_modes_futile.store(true, Ordering::Relaxed);
|
||||||
break;
|
|
||||||
}
|
|
||||||
tracing::debug!(
|
|
||||||
kick,
|
|
||||||
"in-place resize: new mode not yet enumerable — forcing another mode re-enumeration"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if !advertised {
|
|
||||||
anyhow::bail!(
|
anyhow::bail!(
|
||||||
"OS did not advertise {}x{} within {}ms of the driver mode-list update (offers: {:?})",
|
"OS did not advertise {}x{} within {}ms of the driver mode-list update \
|
||||||
|
(offers: {:?}) — latching UPDATE_MODES off for this process",
|
||||||
mode.width,
|
mode.width,
|
||||||
mode.height,
|
mode.height,
|
||||||
t0.elapsed().as_millis(),
|
t0.elapsed().as_millis(),
|
||||||
crate::win_display::advertised_resolutions(&gdi)
|
crate::win_display::advertised_resolutions(&gdi)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
let advertised_ms = t0.elapsed().as_millis() as u64;
|
let advertised_ms = t0.elapsed().as_millis() as u64;
|
||||||
set_active_mode(&gdi, mode);
|
set_active_mode(&gdi, mode);
|
||||||
// Verified-state settle (P0.2): the same committed-state predicate as the create paths. A
|
// Verified-state settle (P0.2): the same committed-state predicate as the create paths. A
|
||||||
|
|||||||
@@ -146,6 +146,41 @@ pub fn reap_orphaned(grace: Duration) -> usize {
|
|||||||
n
|
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.
|
/// Fallback modes appended after the requested mode, so a topology change still has options.
|
||||||
fn default_modes() -> Vec<Mode> {
|
fn default_modes() -> Vec<Mode> {
|
||||||
vec![
|
vec![
|
||||||
@@ -494,6 +529,15 @@ pub fn create_monitor(
|
|||||||
let id = {
|
let id = {
|
||||||
let mut lock = lock_monitors();
|
let mut lock = lock_monitors();
|
||||||
let id = resolve_id(&lock, preferred_id);
|
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 {
|
lock.push(MonitorObject {
|
||||||
object: None,
|
object: None,
|
||||||
id,
|
id,
|
||||||
@@ -598,28 +642,34 @@ pub fn create_monitor(
|
|||||||
Some((id, target_id, luid_low, luid_high))
|
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
|
/// `IOCTL_UPDATE_MODES` (v4): refresh the LIVE monitor's advertised mode list to lead with a new
|
||||||
/// preferred mode (+ the same [`default_modes`] fallbacks ADD produces) and push the new TARGET
|
/// preferred mode and push the new TARGET mode list to the OS via `IddCxMonitorUpdateModes2` —
|
||||||
/// mode list to the OS via `IddCxMonitorUpdateModes2` — the in-place mid-stream resize
|
/// the in-place mid-stream resize (`design/first-frame-and-resize-latency.md` P2). No departure:
|
||||||
/// (`design/first-frame-and-resize-latency.md` P2). No departure: the monitor's OS identity, its
|
/// the monitor's OS identity, its swap-chain worker and the retained frame stash all survive.
|
||||||
/// swap-chain worker and the retained frame stash all survive; the OS re-evaluates the target's
|
/// The `*2` (HDR) DDI matches the `*2` mode/buffer family this driver already requires
|
||||||
/// settable modes and the HOST then CCD-forces the new mode active. The `*2` (HDR) DDI matches the
|
/// (IddCx 1.10), so it adds no new OS floor.
|
||||||
/// `*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
|
/// 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
|
/// ([`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
|
/// 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`]).
|
/// 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 {
|
pub fn update_monitor_modes(session_id: u64, width: u32, height: u32, refresh: u32) -> NTSTATUS {
|
||||||
let mut new_modes = vec![Mode {
|
// Swap the stored list (union — see above) + grab the live handle under the lock.
|
||||||
width,
|
let (object, old_modes, new_modes) = {
|
||||||
height,
|
|
||||||
refresh_rates: vec![refresh],
|
|
||||||
}];
|
|
||||||
new_modes.extend(default_modes());
|
|
||||||
|
|
||||||
// Swap the stored list + grab the live handle under the lock.
|
|
||||||
let (object, old_modes) = {
|
|
||||||
let mut lock = lock_monitors();
|
let mut lock = lock_monitors();
|
||||||
let Some(m) = lock.iter_mut().find(|m| m.session_id == session_id) else {
|
let Some(m) = lock.iter_mut().find(|m| m.session_id == session_id) else {
|
||||||
return crate::STATUS_NOT_FOUND;
|
return crate::STATUS_NOT_FOUND;
|
||||||
@@ -627,8 +677,14 @@ pub fn update_monitor_modes(session_id: u64, width: u32, height: u32, refresh: u
|
|||||||
let Some(object) = m.object else {
|
let Some(object) = m.object else {
|
||||||
return crate::STATUS_NOT_FOUND; // created but not yet arrived — nothing to update
|
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());
|
let old = core::mem::replace(&mut m.modes, new_modes.clone());
|
||||||
(object, old)
|
(object, old, new_modes)
|
||||||
};
|
};
|
||||||
|
|
||||||
// The OS's target-mode list for this monitor (the `*2`/HDR shape, like `monitor_query_modes2`).
|
// The OS's target-mode list for this monitor (the `*2`/HDR shape, like `monitor_query_modes2`).
|
||||||
@@ -668,6 +724,9 @@ pub fn remove_monitor(session_id: u64) -> bool {
|
|||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
let mut entry = lock.remove(pos);
|
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())
|
(entry.object, entry.swap_chain_processor.take())
|
||||||
};
|
};
|
||||||
// Drop the worker FIRST (it joins + deletes the swap-chain), THEN depart the monitor.
|
// Drop the worker FIRST (it joins + deletes the swap-chain), THEN depart the monitor.
|
||||||
|
|||||||
Reference in New Issue
Block a user