From f652617f30625e808b1baff15fc379defcccd1c9 Mon Sep 17 00:00:00 2001 From: enricobuehler Date: Wed, 24 Jun 2026 15:40:42 +0000 Subject: [PATCH] docs(windows-rewrite): M1 step-2 pf-vdisplay port plan (workflow-mapped + critiqued) Record the full driver port plan from the iddcx-driver-port-map workflow: the 11 DDIs to wrap, the 15 IDD_CX_CLIENT_CONFIG callbacks, the DeviceContext-owned state model (single Monitor identity + monitor EvtCleanupCallback RAII), the pf-vdisplay-proto frame transport, and the 8-step CI/box-gated checklist. Fold in the adversarial critique: secure-desktop is a BLOCKING gate (do not retire the WGC relay until proven), define the recreate/concurrency/Reconfigure failure branches, host<->driver protocol_version lockstep. De-risk status: the full IddCx symbol surface + .Size machinery is CI-proven present (ae803b2). Co-Authored-By: Claude Opus 4.8 (1M context) --- docs/windows-host-rewrite.md | 71 ++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/docs/windows-host-rewrite.md b/docs/windows-host-rewrite.md index 45f9c5c..5e956d4 100644 --- a/docs/windows-host-rewrite.md +++ b/docs/windows-host-rewrite.md @@ -701,3 +701,74 @@ swap-chain processor + frame transport, with the clean ownership model (DeviceCo First gate: a probe linking `IddCxStub` and calling `IddCxDeviceInitConfig`/`…Initialize`/ `…AdapterInitAsync` (CI = compile+link). On-glass load + IDD-push stream needs the RTX box (ephemeral — currently down/Proxmox). + +--- + +## 14. M1 step 2 — pf-vdisplay driver port plan (2026-06-24, workflow-mapped + critiqued) + +**Status of the binding (DONE, CI-green):** the wdk-sys `iddcx` binding is proven *complete for the whole +driver*, not just init. `wdk-probe/src/iddcx_surface_assert.rs` (commit `ae803b2`) CI-asserts every `*2`/HDR +struct (`IDDCX_TARGET_MODE2`/`PATH2`/`METADATA2`, `IDARG_*RELEASEANDACQUIREBUFFER2` — which embed +`DISPLAYCONFIG_*`/`LUID`, both of which **resolve from `crate::types`** — no allowlist gap), all 14 inbound +`PFN_IDD_CX_*` callbacks, the `.Size` machinery (`IddStructures`/`IddStructureCount`/ +`IddClientVersionHigherThanFramework`/`_IDDSTRUCTENUM::INDEX_*` — so `IDD_STRUCTURE_SIZE!` is portable), and +`IDDCX_ADAPTER_FLAGS::…CAN_PROCESS_FP16` + `IDDCX_TARGET_CAPS::…HIGH_COLOR_SPACE`. ModuleConsts module +naming: the func/struct enums are `_IDDFUNCENUM`/`_IDDSTRUCTENUM` (underscored tag), but the flag/cap enums +are `IDDCX_ADAPTER_FLAGS`/`IDDCX_TARGET_CAPS` (no underscore). + +### DDIs to wrap (11 — graduate `wdk-probe/src/iddcx_rt.rs` → a `wdk-iddcx` crate) +DeviceInitConfig, DeviceInitialize, AdapterInitAsync (done), MonitorCreate, MonitorArrival, +MonitorDeparture, AdapterSetRenderAdapter, SwapChainSetDevice (`other_is_error`; 0x887A0026→retry), +**SwapChainReleaseAndAcquireBuffer2** (HDR variant only; `other_is_error`; E_PENDING 0x8000000A → wait on +the surface event), SwapChainFinishedProcessingFrame. **Drop** the v1 `ReleaseAndAcquireBuffer` (adapter +always sets FP16). **Defer** the hardware-cursor DDIs (cursor baked into video). + +### Callbacks (15 in `IDD_CX_CLIENT_CONFIG`; `*2` mandatory because FP16) +parse_monitor_description (+`2`), monitor_query_target_modes (+`2`), adapter_commit_modes (+`2`), +adapter_init_finished (stash IDDCX_ADAPTER + start watchdog), monitor_get_default_modes (→NOT_IMPLEMENTED, +we always carry EDID), **query_target_info (→HIGH_COLOR_SPACE), set_gamma_ramp (accept-stub — WITHOUT it +the adapter fails to init), set_default_hdr_metadata (accept-stub)** — the last three are mandatory under +FP16, assign_swap_chain, unassign_swap_chain, device_io_control (the pf-vdisplay-proto control plane). +Plus `EvtDeviceD0Entry` (adapter created HERE, not in DeviceAdd) and two `EvtCleanupCallback`s. + +### State model (the rewrite's core change) +`DeviceContext` OWNS all state — IDDCX_ADAPTER, session_id-keyed monitor map, watchdog, the per-render-LUID +`Direct3DDevice` pool — replacing the oracle's process globals. Reachable from BOTH the WDFDEVICE (strong) +and the IDDCX_ADAPTER object (the adapter-side callbacks need it). `MonitorContext` owns the +`SwapChainProcessor` + `target_id`; **wire `EvtCleanupCallback` on the IDDCX_MONITOR object** so RAII Drop +joins the worker thread + frees D3D (the oracle lacked this → the dominant reconnect leak). **Single Monitor +identity** keyed by `session_id` (collapses the oracle's 3-way EDID-serial/map/stamp desync that caused the +`target_id=0` recreate bug); `assign_swap_chain` reads `target_id` from the context, never a map lookup. +The HOST still owns the control-device handle, the linger/reuse state machine, and ALL `Global\` shared +objects (created `D:(A;;GA;;;WD)`); the driver only OPENS them. + +### Frame transport (single-source on `pf_vdisplay_proto::frame::*`) +Acquire via `ReleaseAndAcquireBuffer2{AcquireSystemMemoryBuffer=0}` → GPU `ID3D11Texture2D`; borrow +`out.MetaData.pSurface` with `IDXGIResource::from_raw_borrowed` (do NOT steal IddCx's refcount), publish +BEFORE `FinishedProcessingFrame`. Ring = `RING_LEN`(6) keyed-mutex shared textures opened by name +(`frame::{header_name,event_name,texture_name}`); per-frame: GetDesc format-guard (drop on FP16↔BGRA +mismatch), `AcquireSync(0,0ms)`, `CopyResource`, `ReleaseSync(0)`, store `FrameToken{gen,seq,slot}.pack()` +(Release), `SetEvent`. All-slots-busy → drop, never block. `is_stale()` (header.generation Acquire) → reattach +on host ring recreate. Write `DRV_STATUS_OPENED` + render LUID into the header. Drop the old DebugBlock + +the locally-duplicated header/MAGIC/name consts. + +### Implementation checklist (each step CI- or box-gated) +0. workspace `pf-vdisplay`(cdylib)+`wdk-iddcx` members — **STEP-0 gate must pull in `std::thread`+`OwnedHandle`** (critique: prove std links under the UMDF toolchain *here*, not at STEP 5). CI. +1. graduate `iddcx_rt.rs` → `wdk-iddcx` (11 DDIs + `is_nt_error`/`other_is_error`) + **re-export the inbound PFN types**. CI link. +1.5 (critique add) the surface-assert (DONE @ `ae803b2`) lives on so the full PFN/`*2`/`DISPLAYCONFIG` surface stays a CI gate. +2. DriverEntry + driver_add: full `IDD_CX_CLIENT_CONFIG` (15 callbacks as stubs) + DeviceInitConfig + WdfDeviceCreate(+cleanup) + CreateDeviceInterface(`PF_VDISPLAY_INTERFACE_GUID`) + DeviceInitialize + D0Entry stub; salvage `edid.rs` verbatim. **Resolve `.Size` via `IDD_STRUCTURE_SIZE!` (machinery confirmed present).** CI link + FORCE_INTEGRITY clear. +3. DeviceContext + `WDF_DECLARE_CONTEXT_TYPE` Arc blob; init_adapter in D0Entry (caps+FP16) → AdapterInitAsync; the `*2` mode DDIs + query_target_info + gamma/hdr accept-stubs. **Box gate:** loads under Secure Boot, enumerates as IddCx adapter, Status OK (no "Failed to get adapter"). +4. control plane (GET_INFO version handshake — **host MUST assert `protocol_version`**, ADD/REMOVE/SET_RENDER_ADAPTER/PING/CLEAR_ALL) + create_monitor + real mode DDIs + watchdog + MONITOR_OP_LOCK; **switch host `sudovda.rs`/`idd_push.rs` to `pf_vdisplay_proto` (GUID e5bcc234→70667664, IOCTL 0x800→0x900, GUID-key→session_id) — lockstep**. CI (host build) + box (monitor appears at WxH@Hz). +5. Direct3DDevice + assign/unassign + SwapChainProcessor (worker thread, SetDevice 60×@50ms single-borrow retry, top-of-loop terminate, Buffer2 acquire, from_raw_borrowed) WITHOUT publisher; wire monitor `EvtCleanupCallback`. **Box:** swap-chain assigns, acquire loop runs, RAII teardown (no thread/VRAM leak). **Critique: instrument that MonitorContext::Drop actually RAN; if the monitor-object cleanup callback does not fire, keep the oracle's explicit free-before-departure path as the fallback.** +6. FramePublisher on `pf_vdisplay_proto::frame::*` + keyed-mutex RAII guard + OwnedHandle/ShmView; wire into run_core. **Box:** full IDD-push glass-to-glass, A/B vs the shipping driver. **Critique: add a BLOCKING secure-desktop gate here** — lock (Win+L)+UAC with serve in the console session / driver in Session 0, confirm frames keep flowing AND input reaches the desktop; until it passes, do NOT delete the WGC-relay/DDA secure path. +7. HDR ring-recreate + repeated session recreate (confirm the recreate-crash is gone). **Critique: define the failure branch** — if recreate isn't stable, keep IDD_PERSIST + state that mid-stream Reconfigure stays unsupported on Windows IDD-push (host rejects, as today) rather than crashing; keep `max_concurrent=1`. **Specify the concurrent-monitor D3D model before enabling >1** (two worker threads must not share one SINGLETHREADED immediate context — give each monitor its own device or a deferred/multithreaded context). +8. unsafe-reduction pass (one audited `SendPtr`/`ThreadBound`; per-site `// SAFETY`; `AcquiredSurface<'_>` + `KeyedMutexGuard` RAII so the hot loop has zero raw Finish/ReleaseSync) + **delete the old `packaging/windows/vdisplay-driver/` tree only after the secure-desktop gate (step 6) passes**. CI clippy -D warnings + final box A/B. + +### Critique verdict + the big risk +Plan is implementation-ready once the 4 CI-checkable unknowns are gates (3 now resolved by the surface-assert ++ `.Size` machinery presence; std-under-UMDF is the STEP-0 gate). **SINGLE BIGGEST RISK: the secure-desktop +claim** — the plan retires the proven two-process WGC relay + DDA on the *unproven* assertion that one +IddPushCapturer captures the lock/UAC secure desktop directly (IDD-push is opt-in today behind +`PUNKTFUNK_IDD_PUSH`). Make it a blocking on-glass gate (step 6) and keep the WGC relay recoverable for one +release. Other defined-failure-branch items: monitor `EvtCleanupCallback` firing, IDD_PERSIST/Reconfigure, +concurrent-monitor device sharing, host↔driver `protocol_version` lockstep.