fix(vdisplay): preserve FramePublisher across swap-chain reassign (sibling-join freeze)
windows-drivers / probe-and-proto (push) Successful in 28s
ci / web (push) Successful in 54s
ci / docs-site (push) Successful in 1m0s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 8s
decky / build-publish (push) Successful in 19s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 8s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 9s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 7s
windows-drivers / driver-build (push) Successful in 1m35s
apple / swift (push) Successful in 4m34s
ci / bench (push) Successful in 5m39s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 6m50s
docker / deploy-docs (push) Successful in 33s
windows-host / package (push) Successful in 10m8s
android / android (push) Successful in 12m24s
arch / build-publish (push) Successful in 16m41s
deb / build-publish (push) Successful in 17m40s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 12m13s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 18m21s
ci / rust (push) Successful in 23m52s
apple / screenshots (push) Successful in 20m9s
windows-drivers / probe-and-proto (push) Successful in 28s
ci / web (push) Successful in 54s
ci / docs-site (push) Successful in 1m0s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 8s
decky / build-publish (push) Successful in 19s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 8s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 9s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 7s
windows-drivers / driver-build (push) Successful in 1m35s
apple / swift (push) Successful in 4m34s
ci / bench (push) Successful in 5m39s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 6m50s
docker / deploy-docs (push) Successful in 33s
windows-host / package (push) Successful in 10m8s
android / android (push) Successful in 12m24s
arch / build-publish (push) Successful in 16m41s
deb / build-publish (push) Successful in 17m40s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 12m13s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 18m21s
ci / rust (push) Successful in 23m52s
apple / screenshots (push) Successful in 20m9s
When a second client got its own virtual display mid-stream, the FIRST client's IDD-push stream froze (video only; `new_fps=0 repeat_fps=240` forever). Adding/removing/resizing a sibling display re-commits the CCD topology, which makes the OS unassign+reassign the first monitor's IddCx swap chain. `unassign_swap_chain` dropped the SwapChainProcessor, dropping `run_core`'s local FramePublisher and closing the sealed-channel handles. The fresh worker then polled the frame-channel stash — but that stash is consumed once at session open, and the host only re-delivers on a ring recreate (a descriptor change). The first monitor's descriptor didn't change and WUDFHost stayed alive, so no watchdog fired: the driver drained the swap chain without publishing and the host repeated its last frame indefinitely. Confirmed twice on the .173 box (host.log 21:12 & 21:15). Preserve the live FramePublisher across the flap instead of dropping it: the host-owned ring (header/event/textures) it holds stays valid — only the swap chain died. - frame_transport.rs: FramePublisher records its render-adapter LUID + exposes render_adapter(). - monitor.rs: MonitorObject.preserved_publisher + preserve_publisher() (mirrors set_frame_channel) + take_preserved_publisher() (mirrors take_frame_channel). Monitor teardown drops the stashed publisher and closes its ring handles, so nothing leaks. - swap_chain_processor.rs run_core: after SetDevice OK, re-adopt a preserved publisher ONLY when the new swap chain renders on the same LUID (same pooled Direct3DDevice → its context + opened textures are valid); on loop exit, stash the publisher back on the monitor. Safe: the old worker is fully joined (drop-outside-lock discipline) before the new one runs, so no concurrent context use; a stale re-adopted publisher is superseded by the existing is_stale() + has_frame_channel() newest-wins checks at the loop top. Verified clippy -D warnings clean on rustc 1.96.0 via a faithful mock crate (the real crate needs the WDK to compile). Needs a driver rebuild + reinstall on the host to take effect; not yet hardware-validated. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -146,6 +146,12 @@ pub struct FramePublisher {
|
||||
/// Set when a surface is dropped for a descriptor mismatch (a game mode-set the display), cleared on a
|
||||
/// matched publish — throttles the drop log to once per mismatch episode (game-capture bug GB1).
|
||||
mismatch_logged: bool,
|
||||
/// The render adapter (LUID) this publisher's device + opened ring textures live on. A worker
|
||||
/// re-adopts a publisher preserved across a swap-chain unassign→reassign flap ONLY when the
|
||||
/// freshly-assigned swap-chain renders on this SAME adapter (else the opened textures would be
|
||||
/// cross-device); see [`Self::render_adapter`] + `swap_chain_processor::run_core`.
|
||||
render_luid_low: u32,
|
||||
render_luid_high: i32,
|
||||
}
|
||||
|
||||
// SAFETY: created and used only on the swap-chain processor thread.
|
||||
@@ -356,6 +362,8 @@ impl FramePublisher {
|
||||
ring_format: unsafe { (*header).dxgi_format },
|
||||
generation: header_gen,
|
||||
mismatch_logged: false,
|
||||
render_luid_low,
|
||||
render_luid_high,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -379,6 +387,13 @@ impl FramePublisher {
|
||||
cur != self.generation
|
||||
}
|
||||
|
||||
/// The render adapter (LUID) this publisher's device + opened ring textures live on. The swap-chain
|
||||
/// worker re-adopts a publisher preserved across an unassign→reassign flap only when the freshly-
|
||||
/// assigned swap-chain renders on this same adapter (see the field docs + `run_core`).
|
||||
pub fn render_adapter(&self) -> (u32, i32) {
|
||||
(self.render_luid_low, self.render_luid_high)
|
||||
}
|
||||
|
||||
/// Copy `surface` into the next free ring slot and signal the host. Never blocks (0 ms try-acquire).
|
||||
pub fn publish(&mut self, surface: &ID3D11Texture2D) {
|
||||
let ring_len = self.slots.len() as u32;
|
||||
|
||||
Reference in New Issue
Block a user