fix(vdisplay/windows): exclusive topology gets re-asserted — a verified isolate is not durable
ci / docs-site (push) Successful in 1m8s
ci / web (push) Successful in 1m40s
apple / swift (push) Successful in 5m9s
ci / bench (push) Successful in 6m36s
ci / rust-arm64 (push) Successful in 10m12s
deb / build-publish (push) Successful in 9m32s
decky / build-publish (push) Successful in 34s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 30s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 59s
android / android (push) Canceled after 13m40s
apple / screenshots (push) Canceled after 8m25s
arch / build-publish (push) Canceled after 13m44s
ci / rust (push) Canceled after 13m45s
deb / build-publish-host (push) Canceled after 12m5s
deb / build-publish-client-arm64 (push) Canceled after 7m5s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Canceled after 2s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Canceled after 0s
docker / build-push-arm64cross (push) Canceled after 0s
docker / deploy-docs (push) Canceled after 0s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Canceled after 2m28s
docker / build-push (ci, ci/rust-ci-noble.Dockerfile, punktfunk-rust-ci-noble) (push) Canceled after 1m57s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Canceled after 2s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Canceled after 1s
windows-host / package (push) Canceled after 13m45s
ci / docs-site (push) Successful in 1m8s
ci / web (push) Successful in 1m40s
apple / swift (push) Successful in 5m9s
ci / bench (push) Successful in 6m36s
ci / rust-arm64 (push) Successful in 10m12s
deb / build-publish (push) Successful in 9m32s
decky / build-publish (push) Successful in 34s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 30s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 59s
android / android (push) Canceled after 13m40s
apple / screenshots (push) Canceled after 8m25s
arch / build-publish (push) Canceled after 13m44s
ci / rust (push) Canceled after 13m45s
deb / build-publish-host (push) Canceled after 12m5s
deb / build-publish-client-arm64 (push) Canceled after 7m5s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Canceled after 2s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Canceled after 0s
docker / build-push-arm64cross (push) Canceled after 0s
docker / deploy-docs (push) Canceled after 0s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Canceled after 2m28s
docker / build-push (ci, ci/rust-ci-noble.Dockerfile, punktfunk-rust-ci-noble) (push) Canceled after 1m57s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Canceled after 2s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Canceled after 1s
windows-host / package (push) Canceled after 13m45s
The CCD isolate verifies "sole active desktop" at commit time and is never checked again. On a hybrid Intel+NVIDIA box the internal panel is re-activated moments later (the isolated topology is deliberately not saved to the CCD database so teardown can restore the user's layout, and the post-isolate resize/HDR churn — or the panel's own driver, or display-poller software — re-resolves back to the stored layout). Proven on-glass: seconds after the verify, CCD showed the panel ACTIVE beside the virtual display while the host still believed it was exclusive — cursor and windows can land off-stream, and the lock screen can land on the physical panel. A group-scoped watchdog now re-queries every PUNKTFUNK_EXCLUSIVE_REASSERT_MS (default 2 s, 0 disables) while an EXCLUSIVE isolate is live and re-issues the isolate when a non-managed display crept back, logging who-is-fighting escalation (3×WARN → 1×ERROR → DEBUG). Gated on a new GroupState.ccd_exclusive flag so a Primary group's deliberately-lit panels are never "fixed". Cycles take the state lock via try_lock with a sliced sleep, so teardown's stop+join under the state lock cannot deadlock and is bounded by ~250 ms. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -179,6 +179,20 @@ pub trait Capturer: Send {
|
||||
fn resize_output(&mut self, _width: u32, _height: u32) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
/// Recreate the delivery ring at the CURRENT mode and re-run the driver attach handshake —
|
||||
/// the recovery half of a swap-chain bounce the descriptor poller cannot see: an
|
||||
/// exclusive-topology eviction (the vdisplay re-assert watchdog) is a real topology change,
|
||||
/// so the OS drives COMMIT_MODES on the live virtual display too and the driver's swap-chain
|
||||
/// is recreated while this capturer keeps waiting on the old ring attachment — frames stop
|
||||
/// with an unchanged descriptor (same mode, same HDR), so the two-strike debounce never
|
||||
/// trips. Arms the same recover-or-drop window as a real resize, so a driver that cannot
|
||||
/// re-attach still fails the session cleanly. Returns `true` when handled; `false` (the
|
||||
/// default) means the backend has no in-place ring recovery and the caller should treat the
|
||||
/// pipeline as unrecoverable in place.
|
||||
fn recreate_ring_in_place(&mut self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// A deterministic moving test pattern (BGRx). Lets the spike exercise the encode → file →
|
||||
|
||||
@@ -1786,6 +1786,49 @@ impl Capturer for IddPushCapturer {
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
fn recreate_ring_in_place(&mut self) -> bool {
|
||||
// Same-mode ring recreate (trait doc: swap-chain bounce recovery) — deliberately NOT
|
||||
// routed through `resize_output`, whose same-size fast path would no-op exactly the
|
||||
// case this exists for. Same recover-or-drop arming as the resize recreate.
|
||||
//
|
||||
// Restart OS presentation FIRST: the eviction's topology commit leaves DWM not
|
||||
// presenting to this display, so a re-attached ring would only ever receive the
|
||||
// driver's stash (measured: new_fps=0 forever after the re-attach). CDS_RESET forces a
|
||||
// real mode-set at the CURRENT mode — the same lever bring-up's ADD path relies on —
|
||||
// and the ring recreate below then re-attaches after that churn, not before it.
|
||||
// SAFETY: `resolve_gdi_name` runs the CCD query FFI over a `Copy` target id (owned
|
||||
// return) — same contract as every sibling call in this file.
|
||||
match unsafe { pf_win_display::win_display::resolve_gdi_name(self.target_id) } {
|
||||
Some(gdi) => {
|
||||
if !pf_win_display::win_display::force_mode_reset(&gdi) {
|
||||
tracing::warn!(
|
||||
target_id = self.target_id,
|
||||
"IDD push: presentation-restart mode reset failed — re-attaching anyway"
|
||||
);
|
||||
}
|
||||
}
|
||||
None => tracing::warn!(
|
||||
target_id = self.target_id,
|
||||
"IDD push: no GDI name for the presentation-restart mode reset — re-attaching \
|
||||
anyway"
|
||||
),
|
||||
}
|
||||
tracing::info!(
|
||||
target_id = self.target_id,
|
||||
mode = format!("{}x{}", self.width, self.height),
|
||||
"IDD push: same-mode ring recreate — re-running the driver attach handshake"
|
||||
);
|
||||
self.recovering_since.get_or_insert_with(Instant::now);
|
||||
if let Err(e) = self.recreate_ring(self.display_hdr, self.width, self.height) {
|
||||
tracing::warn!(
|
||||
error = %format!("{e:#}"),
|
||||
"IDD push: same-mode ring recreate failed"
|
||||
);
|
||||
return false;
|
||||
}
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
/// A 4:4:4 session while the display is HDR: there is no 10-bit full-chroma source (the FP16
|
||||
|
||||
Reference in New Issue
Block a user