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
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:
@@ -63,8 +63,10 @@ const COLORIMETRY_DB: [u8; 4] = [
|
||||
];
|
||||
|
||||
/// HDR Static Metadata Data Block (CTA extended tag 0x06): EOTFs = Traditional SDR (ET_0) + SMPTE ST
|
||||
/// 2084 / PQ (ET_2); Static Metadata Type 1 (SM_0). Plus the optional desired-content luminance hints
|
||||
/// (~993 nit max, ~400 nit max-frame-average, ~0.05 nit min) so the block is complete.
|
||||
/// 2084 / PQ (ET_2); Static Metadata Type 1 (SM_0). Plus the desired-content luminance hints
|
||||
/// (~993 nit max, ~400 nit max-frame-average, ~0.05 nit min) — the BUILT-IN defaults, used when the
|
||||
/// host reported no client volume; [`Edid::generate_with`] overwrites bytes 4..7 with the CLIENT
|
||||
/// display's coded volume otherwise, so host apps tone-map to the panel the stream lands on.
|
||||
#[rustfmt::skip]
|
||||
const HDR_STATIC_METADATA_DB: [u8; 7] = [
|
||||
0xE6, // tag 0b111 (use-extended-tag) | length 6
|
||||
@@ -76,12 +78,30 @@ const HDR_STATIC_METADATA_DB: [u8; 7] = [
|
||||
0x12, // Desired Content Min Luminance (code 18 ≈ 0.05 nits)
|
||||
];
|
||||
|
||||
/// The client display's luminance volume for the CTA HDR block (the [`AddRequest`]
|
||||
/// (pf_driver_proto::control::AddRequest) luminance tail, same units). `max_nits == 0` = unknown
|
||||
/// (an SDR client, or an un-upgraded host whose short ADD zero-fills the tail) → the built-in
|
||||
/// defaults stay.
|
||||
#[derive(Debug, Clone, Copy, Default)]
|
||||
pub struct ClientLuminance {
|
||||
/// Peak luminance, nits. `0` = unknown → keep the built-in default block.
|
||||
pub max_nits: u32,
|
||||
/// Max frame-average luminance, nits. `0` = unknown ("no data" on the wire).
|
||||
pub max_frame_avg_nits: u32,
|
||||
/// Min luminance, milli-nits. `0` = unknown/true black ("no data" on the wire).
|
||||
pub min_millinits: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct Edid;
|
||||
|
||||
impl Edid {
|
||||
/// Build the full 256-byte EDID for monitor `serial`, with both block checksums recomputed.
|
||||
pub fn generate_with(serial: u32) -> Vec<u8> {
|
||||
/// `lum` is the CLIENT display's luminance volume — coded into the HDR static-metadata block's
|
||||
/// desired-content bytes (CTA-861.3, via the shared+unit-tested `pf_driver_proto::edid`
|
||||
/// coders) so the OS/apps tone-map to the client's real panel; all-zero keeps the built-in
|
||||
/// ~993-nit defaults.
|
||||
pub fn generate_with(serial: u32, lum: ClientLuminance) -> Vec<u8> {
|
||||
let mut edid = [0u8; 256];
|
||||
// Block 0: base.
|
||||
edid[..128].copy_from_slice(&BASE);
|
||||
@@ -89,7 +109,18 @@ impl Edid {
|
||||
// Block 1: CTA-861.3 extension (header + colorimetry + HDR static metadata; rest stays 0).
|
||||
edid[128..132].copy_from_slice(&CTA_HEADER);
|
||||
edid[132..136].copy_from_slice(&COLORIMETRY_DB);
|
||||
edid[136..143].copy_from_slice(&HDR_STATIC_METADATA_DB);
|
||||
let mut hdr_db = HDR_STATIC_METADATA_DB;
|
||||
if lum.max_nits > 0 {
|
||||
let max_code = pf_driver_proto::edid::cta_max_luminance_code(lum.max_nits);
|
||||
hdr_db[4] = max_code;
|
||||
hdr_db[5] = if lum.max_frame_avg_nits > 0 {
|
||||
pf_driver_proto::edid::cta_max_luminance_code(lum.max_frame_avg_nits)
|
||||
} else {
|
||||
0 // "no data" — valid per CTA-861.3
|
||||
};
|
||||
hdr_db[6] = pf_driver_proto::edid::cta_min_luminance_code(lum.min_millinits, max_code);
|
||||
}
|
||||
edid[136..143].copy_from_slice(&hdr_db);
|
||||
// Each 128-byte block ends in a checksum byte that makes the block sum ≡ 0 (mod 256).
|
||||
Self::fix_block_checksum(&mut edid, 0);
|
||||
Self::fix_block_checksum(&mut edid, 128);
|
||||
|
||||
Reference in New Issue
Block a user