feat(windows): parallel virtual displays — proto v3 ring binding, manager slot map, group topology (W0–W3)
windows-drivers / probe-and-proto (push) Successful in 41s
ci / web (push) Successful in 1m2s
ci / docs-site (push) Successful in 1m7s
apple / swift (push) Successful in 1m11s
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 15s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 9s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 7s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 6s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 38s
windows-drivers / driver-build (push) Successful in 1m46s
ci / bench (push) Successful in 6m35s
docker / deploy-docs (push) Successful in 20s
windows-host / package (push) Successful in 9m37s
deb / build-publish (push) Successful in 13m48s
arch / build-publish (push) Successful in 14m21s
windows-msix / package (arm64, C:\Users\Public\ffmpeg-arm64, --no-default-features, aarch64-pc-windows-msvc, C:\t-a64) (push) Successful in 2m24s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 13m34s
android / android (push) Successful in 15m45s
windows-msix / package (x64, C:\Users\Public\ffmpeg, , x86_64-pc-windows-msvc, C:\t) (push) Successful in 2m20s
windows / build (aarch64-pc-windows-msvc) (push) Successful in 1m18s
ci / rust (push) Successful in 18m48s
windows / build (x86_64-pc-windows-msvc) (push) Successful in 1m41s
release / apple (push) Successful in 19m30s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 16m49s
flatpak / build-publish (push) Failing after 2m9s
apple / screenshots (push) Successful in 19m3s

design/windows-parallel-virtual-displays.md (display-management Stage 7 / §6.6): N
simultaneously-live pf-vdisplay monitors, one sealed ring each, every idd-push-security
invariant preserved per-ring.

- proto v3: SharedHeader._pad → target_id — the ring NAMES its monitor, host-stamped
  before the magic; the driver publisher refuses a cross-bound ring via the shared,
  unit-tested frame::check_attach (new DRV_STATUS_BIND_FAIL — the gamepad pad_index
  validation applied to frames, invariant #10); the host's wait_for_attach surfaces the
  refusal loudly and self-checks its own stamp.
- manager: the one-monitor MgrState becomes a slot map keyed by the client's identity
  slot (0 = anonymous/GameStream); per-slot reconnect + dead-WUDFHost preempts,
  slot-scoped begin_idd_setup (a different identity is an admission question, never a
  preempt), ONE device-level watchdog pinger, per-slot /display/state + /display/release.
- group topology: isolate_displays_ccd takes the managed target SET (a sibling slot is
  never deactivated); SavedConfig + the DDC/PnP axes move to the group record (first-in
  captures, last-out restores); desktop layout via CCD source origins from the pure
  layout::arrange (auto-row default, manual pins win), re-applied on create + reconfigure.
- admission: the Windows separate→reject override now sits behind the
  PUNKTFUNK_WIN_SEPARATE=1 validation hatch (the wedge it guarded is structurally gone —
  a second identity gets its own monitor + ring; default flips in W5 after soak);
  max_displays and NVENC session-unit budgets decline an unaffordable display AT
  admission; kick_dwm_compose is process-globally throttled and per-display — cursor
  jump + 35 ms dwell (a sub-tick jump composes nothing; DWM reads dirties from current
  state at the next vsync tick).

On-glass on the RTX box: V1/V2/V4/V5/V6/V9 green — two paired clients on two monitors
streaming ~60 fps each with zero mismatches and zero bind failures, churn-hammer clean
(no 0x80070490), per-ring mode-change recreate leaves the sibling untouched, typed
budget rejection, fault-injected cross-bind refused loudly with the sibling undisturbed.
V7: WUDFHost-kill shared fate is clean; in-process device recovery is a known follow-up
(the retired-never-closed control handles block the adapter cycle — reset-pf-vdisplay.ps1
recovers). DWM composes two IDD monitors concurrently at 60 fps — the plan's
load-bearing unknown, answered yes.

Also carries the client-HDR EDID forwarding that shared this working tree
(Hello::display_hdr → AddRequest luminance tail → the monitor's CTA-861.3 HDR block,
PUNKTFUNK_CLIENT_PEAK_NITS hatch) and the Deck client fixes (40 ms rumble keep-alive
with 1-LSB jitter, HDR self-diagnosing presenter warn, flatpak HDR env).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-11 01:06:35 +02:00
parent 979e38523b
commit 35d97ae6ac
34 changed files with 1945 additions and 544 deletions
+70
View File
@@ -119,6 +119,7 @@ fn codec_negotiation_and_back_compat() {
audio_channels: 2, // stereo — forces the video_caps/audio_channels placeholders
video_codecs: CODEC_H264 | CODEC_HEVC,
preferred_codec: CODEC_H264,
display_hdr: None,
};
let enc = h.encode();
let dec = Hello::decode(&enc).unwrap();
@@ -234,6 +235,7 @@ fn hello_start_roundtrip() {
audio_channels: 2,
video_codecs: CODEC_H264 | CODEC_HEVC, // exercise the codec bitfield roundtrip
preferred_codec: CODEC_HEVC,
display_hdr: None,
};
assert_eq!(Hello::decode(&h.encode()).unwrap(), h);
let s = Start {
@@ -316,6 +318,7 @@ fn hello_welcome_compositor_back_compat() {
audio_channels: 2,
video_codecs: 0,
preferred_codec: 0,
display_hdr: None,
};
let enc = h.encode();
assert_eq!(enc.len(), 26);
@@ -430,6 +433,7 @@ fn hello_name_roundtrip_and_back_compat() {
audio_channels: 2,
video_codecs: 0,
preferred_codec: 0,
display_hdr: None,
};
let enc = base.encode();
assert_eq!(
@@ -480,6 +484,7 @@ fn hello_launch_roundtrip_and_back_compat() {
audio_channels: 2,
video_codecs: 0,
preferred_codec: 0,
display_hdr: None,
};
// launch alone (no name): a zero-length name placeholder keeps the offset deterministic.
let with_launch = Hello {
@@ -520,6 +525,70 @@ fn hello_launch_roundtrip_and_back_compat() {
assert!(dec.len() <= HELLO_LAUNCH_MAX && dec.starts_with('x'));
}
#[test]
fn hello_display_hdr_roundtrip_and_back_compat() {
let base = Hello {
abi_version: 2,
mode: Mode {
width: 3840,
height: 2160,
refresh_hz: 120,
},
compositor: CompositorPref::Auto,
gamepad: GamepadPref::Auto,
bitrate_kbps: 0,
name: None,
launch: None,
video_caps: VIDEO_CAP_10BIT | VIDEO_CAP_HDR,
audio_channels: 2,
video_codecs: 0,
preferred_codec: 0,
display_hdr: None,
};
// A real client-panel volume (P3 primaries, 800-nit peak, 0.05-nit floor, 400-nit FALL).
let vol = HdrMeta {
display_primaries: [[13250, 34500], [7500, 3000], [34000, 16000]], // G, B, R
white_point: [15635, 16450], // D65
max_display_mastering_luminance: 8_000_000, // 800 nits
min_display_mastering_luminance: 500, // 0.05 nits
max_cll: 0,
max_fall: 400,
};
let with_hdr = Hello {
display_hdr: Some(vol),
..base.clone()
};
// Full roundtrip, including the forced placeholders for the earlier trailing fields.
assert_eq!(Hello::decode(&with_hdr.encode()).unwrap(), with_hdr);
// display_hdr alone (every earlier optional at its default) still lands at a deterministic
// offset — the placeholder discipline holds through the whole tail.
let hdr_only = Hello {
video_caps: 0,
display_hdr: Some(vol),
..base.clone()
};
assert_eq!(Hello::decode(&hdr_only.encode()).unwrap(), hdr_only);
// An older host reading a display_hdr-bearing Hello ignores the trailing block (its decode
// stops at preferred_codec); a new host reading an older client's Hello gets None.
let enc = with_hdr.encode();
assert_eq!(
Hello::decode(&enc[..enc.len() - HDR_META_BODY_LEN]).unwrap(),
Hello {
display_hdr: None,
..with_hdr.clone()
}
);
assert_eq!(Hello::decode(&base.encode()).unwrap().display_hdr, None);
// A TRUNCATED trailing block (mid-datagram cut) degrades to None, never a partial read.
assert_eq!(
Hello::decode(&enc[..enc.len() - 1]).unwrap().display_hdr,
None
);
// Exact wire length: 26 bitrate-era bytes + the 6 forced single-byte placeholders
// (name len, launch len, video_caps, audio_channels, video_codecs, preferred_codec) + the body.
assert_eq!(hdr_only.encode().len(), 26 + 6 + HDR_META_BODY_LEN);
}
#[test]
fn reconfigure_roundtrip() {
let rq = Reconfigure {
@@ -786,6 +855,7 @@ fn control_messages_disjoint_from_hello() {
audio_channels: 2,
video_codecs: 0,
preferred_codec: 0,
display_hdr: None,
}
.encode();
assert!(PairRequest::decode(&h).is_err(), "abi {abi} parsed as pair");