Files
punktfunk/packaging/windows/drivers/pf-vdisplay/src/adapter.rs
T
enricobuehler 6399d2817d
apple / swift (push) Failing after 4s
apple / screenshots (push) Has been skipped
windows-drivers / probe-and-proto (push) Successful in 18s
ci / rust (push) Successful in 1m13s
windows-drivers / driver-build (push) Successful in 1m9s
ci / web (push) Successful in 42s
ci / docs-site (push) Successful in 59s
android / android (push) Successful in 3m16s
decky / build-publish (push) Successful in 10s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 6s
deb / build-publish (push) Successful in 2m37s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 4s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 5s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 4s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 3s
windows-host / package (push) Successful in 5m25s
ci / bench (push) Successful in 4m39s
rpm / build-publish (bazzite, punktfunk-fedora-rpm) (push) Successful in 8m29s
docker / deploy-docs (push) Successful in 17s
rpm / build-publish (fedora-44, punktfunk-fedora44-rpm) (push) Successful in 8m12s
feat(windows-drivers): STEP 7 — HDR/FP16 (validated on-glass: Mac connects WITH HDR)
The pf-vdisplay driver now advertises HDR/FP16 and the full glass-to-glass HDR path works
end-to-end — validated LIVE: the Mac client connected to the .173 host WITH HDR (display_hdr=true,
FP16 ring -> NVENC P010). The STEP-3 assumption that FP16 needs a higher UmdfExtensions was WRONG:
IddCx0102 + CAN_PROCESS_FP16 + the *2 DDIs works (the oracle proved it; confirmed on-glass
IddCxAdapterInitAsync -> 0x0 WITH the FP16 cap set). Driver-only change — the host FP16-ring ->
NVENC-P010 path and the HDR EDID were already in place.

- adapter.rs: caps.Flags = IDDCX_ADAPTER_FLAGS_CAN_PROCESS_FP16.
- entry.rs: register the 6 *2/HDR callbacks (ParseMonitorDescription2, MonitorQueryTargetModes2,
  AdapterCommitModes2, AdapterQueryTargetInfo, MonitorSetDefaultHdrMetaData, MonitorSetGammaRamp)
  ALONGSIDE the v1 set (matching the oracle — CAN_PROCESS_FP16 OBLIGATES the *2 DDIs or the
  framework rejects the adapter at init; STEP 3 rejected FP16 only because they weren't registered).
- callbacks.rs: parse_monitor_description2 + monitor_query_modes2 now fill IDDCX_MONITOR_MODE2 /
  IDDCX_TARGET_MODE2 with BitsPerComponent (8|10 bpc RGB); query_target_info already reports
  IDDCX_TARGET_CAPS_HIGH_COLOR_SPACE; set_default_hdr_metadata + set_gamma_ramp accept (the gamma
  one is mandatory under FP16).
- monitor.rs: wire_bits() (Rgb 8|10, no YCbCr) + target_mode2().
- EDID + INF UNCHANGED (the EDID already carries the CTA-861.3 BT.2020 + ST.2084/PQ block; the INF
  stays UmdfExtensions=IddCx0102).

Built via the ultracode flow (STEP-7 map workflow -> agent-implement -> box build [driver green] ->
deploy -> on-glass HDR). OPERATIONAL NOTE: do NOT Disable/Enable the IddCx devnode to reload it —
that leaves the adapter STOPPED in the persisted WUDFHost process (ADAPTER OnceLock survives), so
monitor-create then fails with 0xc00002b6 (INDIRECT_DISPLAY_DEVICE_STOPPED). Kill the pf_vdisplay
WUDFHost process (or reboot) for a clean adapter re-init.

This completes the pf-vdisplay rewrite STEP 0-7, all on-glass validated (loads, adapter inits,
monitor appears, swap-chain drain, IDD-push frames at ~235fps, and HDR). Remaining: STEP 8 (unsafe-
reduction + delete the old vdisplay-driver tree + the vendored SudoVDA driver + unbundle from the
installer = the SudoVDA drop).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 11:31:28 +00:00

133 lines
7.3 KiB
Rust

//! IddCx adapter bring-up. Adapter creation is DEFERRED to the first `EvtDeviceD0Entry` (the adapter
//! object is only valid after D0), and is ASYNC: `init_adapter` builds the caps and calls
//! `IddCxAdapterInitAsync`; the adapter object arrives later via `EvtIddCxAdapterInitFinished`
//! (`adapter_init_finished` → [`set_adapter`]). FP16 caps + the obligated `*2`/gamma/hdr callbacks (in
//! `callbacks.rs`) together enable HDR. STEP 3.
use std::sync::OnceLock;
use wdk_sys::{NTSTATUS, WDFDEVICE, iddcx};
use crate::STATUS_SUCCESS;
/// A static, null-terminated UTF-16 string pointer (ASCII only) — wdk-sys has no `windows` `w!`.
macro_rules! wstr {
($s:literal) => {{
const N: usize = $s.len() + 1;
// `static` (NOT `const`) — a const's `.as_ptr()` points to a temporary dropped at the end of the
// statement (a dangling pointer); IddCx then reads garbage for the endpoint name strings and
// IddCxAdapterInitAsync fails INVALID_PARAMETER. A `static` has a stable 'static address.
static W: [u16; N] = {
let b = $s.as_bytes();
let mut w = [0u16; N];
let mut i = 0;
while i < b.len() {
w[i] = b[i] as u16;
i += 1;
}
w
};
W.as_ptr()
}};
}
/// The IddCx adapter handle, stashed for later DDIs (e.g. `SET_RENDER_ADAPTER`, STEP 4).
struct SendAdapter(iddcx::IDDCX_ADAPTER);
// SAFETY: an opaque IddCx handle, used only as an argument to IddCx DDIs (themselves the synchronisation
// point) — never dereferenced in Rust. Storing it across threads in a OnceLock is sound.
unsafe impl Send for SendAdapter {}
unsafe impl Sync for SendAdapter {}
static ADAPTER: OnceLock<SendAdapter> = OnceLock::new();
/// A WDF context type for the adapter object (matches the upstream's `init_context_type`); STEP 4 stores
/// adapter state here. `WDF_OBJECT_CONTEXT_TYPE_INFO` holds raw pointers (so a Sync wrapper to allow a
/// `static`); `UniqueType` self-references per `WDF_DECLARE_CONTEXT_TYPE`.
#[repr(transparent)]
struct CtxTypeInfo(wdk_sys::WDF_OBJECT_CONTEXT_TYPE_INFO);
// SAFETY: immutable 'static type metadata; the inner raw pointers are 'static and never written.
unsafe impl Sync for CtxTypeInfo {}
static ADAPTER_CTX: CtxTypeInfo = CtxTypeInfo(wdk_sys::WDF_OBJECT_CONTEXT_TYPE_INFO {
Size: core::mem::size_of::<wdk_sys::WDF_OBJECT_CONTEXT_TYPE_INFO>() as u32,
ContextName: c"PfVdAdapterCtx".as_ptr().cast(),
ContextSize: core::mem::size_of::<iddcx::IDDCX_ADAPTER>(),
UniqueType: &ADAPTER_CTX.0,
EvtDriverGetUniqueContextType: None,
});
/// Build the adapter caps (FP16/HDR-capable) and kick off the async adapter creation. Called from
/// `EvtDeviceD0Entry`; idempotent across re-entrant D0 transitions.
pub fn init_adapter(device: WDFDEVICE) -> NTSTATUS {
if ADAPTER.get().is_some() {
return STATUS_SUCCESS;
}
dbglog!("[pf-vd] init_adapter");
// Firmware/hardware version (telemetry). The oracle points BOTH at one IDDCX_ENDPOINT_VERSION.
// `version` is a stack local read synchronously by IddCxAdapterInitAsync (same as the oracle). `.Size`
// is `size_of` throughout — these are the IddCx 1.10 structs and the framework here is 1.10 (= upstream).
let mut version: iddcx::IDDCX_ENDPOINT_VERSION = unsafe { core::mem::zeroed() };
version.Size = core::mem::size_of::<iddcx::IDDCX_ENDPOINT_VERSION>() as u32;
version.MajorVer = env!("CARGO_PKG_VERSION_MAJOR").parse().unwrap_or(0);
version.MinorVer = env!("CARGO_PKG_VERSION_MINOR").parse().unwrap_or(0);
version.Build = env!("CARGO_PKG_VERSION_PATCH").parse().unwrap_or(0);
// Endpoint diagnostics. `pEndPointModelName` must be a non-empty string. GammaSupport MUST be set: a
// zeroed value is IDDCX_FEATURE_IMPLEMENTATION_UNINITIALIZED (0), which the framework's adapter Validate
// rejects with INVALID_PARAMETER (ddivalidation.cpp:797) — set it to NONE (1) like upstream. THIS was
// the on-glass adapter-init blocker.
let mut diag: iddcx::IDDCX_ENDPOINT_DIAGNOSTIC_INFO = unsafe { core::mem::zeroed() };
diag.Size = core::mem::size_of::<iddcx::IDDCX_ENDPOINT_DIAGNOSTIC_INFO>() as u32;
diag.GammaSupport = iddcx::IDDCX_FEATURE_IMPLEMENTATION::IDDCX_FEATURE_IMPLEMENTATION_NONE;
diag.TransmissionType = iddcx::IDDCX_TRANSMISSION_TYPE::IDDCX_TRANSMISSION_TYPE_WIRED_OTHER;
diag.pEndPointFriendlyName = wstr!("punktfunk Virtual Display Adapter");
diag.pEndPointManufacturerName = wstr!("punktfunk");
diag.pEndPointModelName = wstr!("Virtual Display");
diag.pFirmwareVersion = (&raw mut version).cast();
diag.pHardwareVersion = (&raw mut version).cast();
let mut caps: iddcx::IDDCX_ADAPTER_CAPS = unsafe { core::mem::zeroed() };
caps.Size = core::mem::size_of::<iddcx::IDDCX_ADAPTER_CAPS>() as u32;
// STEP 7 (HDR): declare we can process FP16 (scRGB) desktop surfaces — this is what marks the virtual
// monitor advanced-color-capable (→ the host sees display_hdr=true → the "Use HDR" toggle appears). The
// ONLY reason STEP 3 rejected this flag was setting it WITHOUT the obligated *2/HDR DDIs; those are now
// registered in entry.rs (parse_monitor_description2/monitor_query_modes2/adapter_commit_modes2 +
// query_target_info/set_default_hdr_metadata/set_gamma_ramp). The proven oracle sets exactly this flag
// with the INF still at UmdfExtensions=IddCx0102. GammaSupport stays NONE (set above). Enum is bindgen
// ModuleConsts — the variant is a plain-int const assignable straight to the `Flags` field.
caps.Flags = iddcx::IDDCX_ADAPTER_FLAGS::IDDCX_ADAPTER_FLAGS_CAN_PROCESS_FP16;
caps.MaxMonitorsSupported = 16;
caps.EndPointDiagnostics = diag;
// The adapter WDF object's attributes: Size + Synchronization/Execution = InheritFromParent (NOT zeroed,
// since zero = *Invalid*) + the adapter context type (STEP 4 stores adapter state here).
let mut attr: wdk_sys::WDF_OBJECT_ATTRIBUTES = unsafe { core::mem::zeroed() };
attr.Size = core::mem::size_of::<wdk_sys::WDF_OBJECT_ATTRIBUTES>() as u32;
attr.ExecutionLevel = wdk_sys::_WDF_EXECUTION_LEVEL::WdfExecutionLevelInheritFromParent;
attr.SynchronizationScope =
wdk_sys::_WDF_SYNCHRONIZATION_SCOPE::WdfSynchronizationScopeInheritFromParent;
attr.ContextTypeInfo = &ADAPTER_CTX.0;
let init = iddcx::IDARG_IN_ADAPTER_INIT {
WdfDevice: device,
pCaps: &raw mut caps,
ObjectAttributes: &raw mut attr,
};
let mut out: iddcx::IDARG_OUT_ADAPTER_INIT = unsafe { core::mem::zeroed() };
// SAFETY: `init`/`out` are valid local storage; IddCxAdapterInitAsync reads the caps synchronously
// (the adapter object itself is delivered later via adapter_init_finished). Called once per device.
let st = unsafe { wdk_iddcx::IddCxAdapterInitAsync(&init, &mut out) };
dbglog!("[pf-vd] IddCxAdapterInitAsync -> {st:#x}");
st
}
/// Stash the adapter object delivered by `EvtIddCxAdapterInitFinished` (STEP 4 reads it).
pub fn set_adapter(adapter: iddcx::IDDCX_ADAPTER) {
let _ = ADAPTER.set(SendAdapter(adapter));
}
/// The created adapter handle, once `EvtIddCxAdapterInitFinished` has fired — for `create_monitor`
/// (`IddCxMonitorCreate`) and SET_RENDER_ADAPTER. `None` before adapter init completes.
pub(crate) fn adapter() -> Option<iddcx::IDDCX_ADAPTER> {
ADAPTER.get().map(|a| a.0)
}