feat(windows-drivers): STEP 1 — wdk-iddcx with all 11 IddCx DDI wrappers
apple / swift (push) Failing after 2s
apple / screenshots (push) Has been skipped
windows-drivers / probe-and-proto (push) Successful in 18s
windows-host / package (push) Successful in 5m48s
windows-drivers / driver-build (push) Successful in 1m4s
android / android (push) Successful in 3m37s
ci / web (push) Successful in 53s
ci / docs-site (push) Successful in 1m3s
ci / rust (push) Successful in 4m21s
deb / build-publish (push) Successful in 2m16s
decky / build-publish (push) Successful in 11s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 5s
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 3s
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
ci / bench (push) Successful in 4m37s
rpm / build-publish (bazzite, punktfunk-fedora-rpm) (push) Successful in 8m47s
rpm / build-publish (fedora-44, punktfunk-fedora44-rpm) (push) Successful in 8m40s
docker / deploy-docs (push) Successful in 6s

Graduate the proven iddcx_rt.rs dispatch into wdk-iddcx + add the full DDI set the
pf-vdisplay driver needs: DeviceInitConfig/Initialize, AdapterInitAsync,
MonitorCreate/Arrival/Departure, AdapterSetRenderAdapter (void-returning DDI — its
PFN returns ()), SwapChainSetDevice/ReleaseAndAcquireBuffer2/FinishedProcessingFrame.
One dispatch macro pins each (_IDDFUNCENUM index, PFN_* type) pair exactly once
(the only place table dispatch can be UB). Box-compiles green; IddCxStub link gets
validated when pf-vdisplay (cdylib) consumes it in STEP 2.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-24 15:55:45 +00:00
parent d7a9fbf0b6
commit 788e4acbb5
+145 -4
View File
@@ -1,11 +1,152 @@
//! Safe-ish typed wrappers over the wdk-sys IddCx DDIs, dispatched through the `IddFunctions` table
//! (indexed by `_IDDFUNCENUM::<Name>TableIndex`, with `IddDriverGlobals` as the implicit first arg) —
//! the same model wdk-sys uses for WDF. Graduates the proven dispatch from `wdk-probe/src/iddcx_rt.rs`
//! (M1 step 1) into the crate the pf-vdisplay driver builds on.
//! (M1 step 1) and adds the full DDI set the pf-vdisplay driver needs (M1 step 2 / §14).
//!
//! STEP 0: re-export the wdk-sys `iddcx` module so the crate + the `iddcx` feature resolve in the
//! workspace. STEP 1 adds the 11 typed DDI wrappers (one fixed `(_IDDFUNCENUM index, PFN_*)` pair per
//! site) + `is_nt_error`/`other_is_error` + the inbound `PFN_IDD_CX_*` callback-type re-exports.
//! Each DDI pins its `(_IDDFUNCENUM index, PFN_* type)` pair in exactly ONE place (the macro invocation)
//! — a wrong pairing is the only way the table dispatch can be UB, so it must never be expressed twice
//! (port-plan unsafe_hotspot #1). All wrappers return the raw `NTSTATUS`; the caller classifies it with
//! [`nt_success`] (or, for the HRESULT-shaped SwapChain DDIs, treats a nonzero as the documented retry
//! code — handled at the call site in STEP 5).
#![no_std]
#![allow(non_snake_case, clippy::missing_safety_doc)]
pub use wdk_sys::iddcx;
use wdk_sys::{NTSTATUS, PWDFDEVICE_INIT, WDFDEVICE};
/// `NT_SUCCESS` — IddCx DDIs that return a true NTSTATUS are errors iff negative.
#[inline]
#[must_use]
pub const fn nt_success(status: NTSTATUS) -> bool {
status >= 0
}
/// Read the IddCx DDI at `index` from the stub-provided `IddFunctions` table and reinterpret it as the
/// concrete `PFN_*` type. `IddFunctions` is a flexible array (`[PFN_IDD_CX; 0]`) populated by IddCxStub
/// once the driver is loaded.
///
/// # Safety
/// `index`/`T` must be the matching `_IDDFUNCENUM` index and `PFN_*` type for the same DDI, and the
/// table must be populated (true after the driver is loaded by the IddCx runtime).
#[inline]
unsafe fn ddi<T: Copy>(index: i32) -> T {
let table = (&raw const iddcx::IddFunctions).cast::<iddcx::PFN_IDD_CX>();
// SAFETY: `index` is a valid IddCx table slot; the slot holds a `PFN_*` whose layout is `T`.
let slot = unsafe { table.add(index as usize) };
unsafe { slot.cast::<T>().read() }
}
/// The IddCx driver globals (set by `IddCxStub`), passed as the implicit first arg to every DDI.
///
/// # Safety
/// Only valid once the driver is loaded by the IddCx runtime.
#[inline]
unsafe fn globals() -> iddcx::PIDD_DRIVER_GLOBALS {
// SAFETY: we only read the pointer value of the stub-provided global.
unsafe { (&raw const iddcx::IddDriverGlobals).read() }
}
/// Generate a typed outbound-DDI wrapper. Body is identical for every DDI (table lookup → call with the
/// globals); only the `(index, PFN_*, args)` triple varies, and it appears here exactly once.
macro_rules! iddcx_ddi {
(
$(#[$meta:meta])*
$name:ident ( $( $arg:ident : $aty:ty ),* $(,)? ) @ $idx:ident as $pfn:ident
) => {
$(#[$meta])*
///
/// # Safety
/// Call only after the driver is loaded by IddCx; pointers must satisfy the IddCx contract.
#[inline]
pub unsafe fn $name( $( $arg: $aty ),* ) -> NTSTATUS {
let f: iddcx::$pfn = unsafe { ddi(iddcx::_IDDFUNCENUM::$idx) };
let g = unsafe { globals() };
// SAFETY: dispatching a populated DDI with the stub globals and caller-valid args.
unsafe { (f.unwrap())(g, $( $arg ),* ) }
}
};
// void-returning variant (e.g. IddCxAdapterSetRenderAdapter): the DDI's PFN returns `()`.
(
$(#[$meta:meta])*
$name:ident ( $( $arg:ident : $aty:ty ),* $(,)? ) @ $idx:ident as $pfn:ident -> ()
) => {
$(#[$meta])*
///
/// # Safety
/// Call only after the driver is loaded by IddCx; pointers must satisfy the IddCx contract.
#[inline]
pub unsafe fn $name( $( $arg: $aty ),* ) {
let f: iddcx::$pfn = unsafe { ddi(iddcx::_IDDFUNCENUM::$idx) };
let g = unsafe { globals() };
// SAFETY: dispatching a populated DDI with the stub globals and caller-valid args.
unsafe { (f.unwrap())(g, $( $arg ),* ) }
}
};
}
iddcx_ddi!(
/// Configure a `WDFDEVICE_INIT` for IddCx (call before `WdfDeviceCreate`).
IddCxDeviceInitConfig(device_init: PWDFDEVICE_INIT, config: *const iddcx::IDD_CX_CLIENT_CONFIG)
@ IddCxDeviceInitConfigTableIndex as PFN_IDDCXDEVICEINITCONFIG
);
iddcx_ddi!(
/// Finish IddCx device init (call after `WdfDeviceCreate`).
IddCxDeviceInitialize(device: WDFDEVICE)
@ IddCxDeviceInitializeTableIndex as PFN_IDDCXDEVICEINITIALIZE
);
iddcx_ddi!(
/// Create the WDDM adapter asynchronously; `out.AdapterObject` is the `IDDCX_ADAPTER`.
IddCxAdapterInitAsync(
in_args: *const iddcx::IDARG_IN_ADAPTER_INIT,
out_args: *mut iddcx::IDARG_OUT_ADAPTER_INIT,
) @ IddCxAdapterInitAsyncTableIndex as PFN_IDDCXADAPTERINITASYNC
);
iddcx_ddi!(
/// Create a monitor on the adapter; `out.MonitorObject` is the `IDDCX_MONITOR`.
IddCxMonitorCreate(
adapter: iddcx::IDDCX_ADAPTER,
in_args: *const iddcx::IDARG_IN_MONITORCREATE,
out_args: *mut iddcx::IDARG_OUT_MONITORCREATE,
) @ IddCxMonitorCreateTableIndex as PFN_IDDCXMONITORCREATE
);
iddcx_ddi!(
/// Announce monitor arrival; `out.OsTargetId`/`out.OsAdapterLuid` come back.
IddCxMonitorArrival(
monitor: iddcx::IDDCX_MONITOR,
out_args: *mut iddcx::IDARG_OUT_MONITORARRIVAL,
) @ IddCxMonitorArrivalTableIndex as PFN_IDDCXMONITORARRIVAL
);
iddcx_ddi!(
/// Depart a monitor (host disconnect / session teardown).
IddCxMonitorDeparture(monitor: iddcx::IDDCX_MONITOR)
@ IddCxMonitorDepartureTableIndex as PFN_IDDCXMONITORDEPARTURE
);
iddcx_ddi!(
/// Set the preferred render adapter (LUID) for the virtual adapter.
IddCxAdapterSetRenderAdapter(
adapter: iddcx::IDDCX_ADAPTER,
in_args: *const iddcx::IDARG_IN_ADAPTERSETRENDERADAPTER,
) @ IddCxAdapterSetRenderAdapterTableIndex as PFN_IDDCXADAPTERSETRENDERADAPTER -> ()
);
iddcx_ddi!(
/// Bind a D3D device to an assigned swap-chain. HRESULT-shaped (0x887A0026 → retry on monitor flap).
IddCxSwapChainSetDevice(
swap_chain: iddcx::IDDCX_SWAPCHAIN,
in_args: *const iddcx::IDARG_IN_SWAPCHAINSETDEVICE,
) @ IddCxSwapChainSetDeviceTableIndex as PFN_IDDCXSWAPCHAINSETDEVICE
);
iddcx_ddi!(
/// Release the previous frame and acquire the next (HDR/FP16 variant). HRESULT-shaped; E_PENDING
/// (0x8000000A) means wait on the surface-available event.
IddCxSwapChainReleaseAndAcquireBuffer2(
swap_chain: iddcx::IDDCX_SWAPCHAIN,
in_args: *mut iddcx::IDARG_IN_RELEASEANDACQUIREBUFFER2,
out_args: *mut iddcx::IDARG_OUT_RELEASEANDACQUIREBUFFER2,
) @ IddCxSwapChainReleaseAndAcquireBuffer2TableIndex as PFN_IDDCXSWAPCHAINRELEASEANDACQUIREBUFFER2
);
iddcx_ddi!(
/// Signal that the acquired frame has been processed. HRESULT-shaped.
IddCxSwapChainFinishedProcessingFrame(swap_chain: iddcx::IDDCX_SWAPCHAIN)
@ IddCxSwapChainFinishedProcessingFrameTableIndex as PFN_IDDCXSWAPCHAINFINISHEDPROCESSINGFRAME
);