feat(windows-drivers): STEP 3 — IddCx adapter init (deferred D0, FP16 caps)
apple / swift (push) Failing after 1s
apple / screenshots (push) Has been skipped
windows-drivers / probe-and-proto (push) Successful in 18s
windows-drivers / driver-build (push) Successful in 1m5s
windows-host / package (push) Successful in 5m13s
android / android (push) Successful in 3m42s
ci / web (push) Successful in 53s
ci / docs-site (push) Successful in 1m3s
ci / rust (push) Successful in 4m27s
deb / build-publish (push) Successful in 2m14s
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 4s
ci / bench (push) Successful in 4m41s
rpm / build-publish (bazzite, punktfunk-fedora-rpm) (push) Successful in 8m33s
rpm / build-publish (fedora-44, punktfunk-fedora44-rpm) (push) Successful in 8m24s
docker / deploy-docs (push) Successful in 17s
apple / swift (push) Failing after 1s
apple / screenshots (push) Has been skipped
windows-drivers / probe-and-proto (push) Successful in 18s
windows-drivers / driver-build (push) Successful in 1m5s
windows-host / package (push) Successful in 5m13s
android / android (push) Successful in 3m42s
ci / web (push) Successful in 53s
ci / docs-site (push) Successful in 1m3s
ci / rust (push) Successful in 4m27s
deb / build-publish (push) Successful in 2m14s
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 4s
ci / bench (push) Successful in 4m41s
rpm / build-publish (bazzite, punktfunk-fedora-rpm) (push) Successful in 8m33s
rpm / build-publish (fedora-44, punktfunk-fedora44-rpm) (push) Successful in 8m24s
docker / deploy-docs (push) Successful in 17s
adapter.rs: init_adapter(device) builds IDDCX_ADAPTER_CAPS (CAN_PROCESS_FP16, MaxMonitorsSupported=16, endpoint diagnostics with wstr! PCWSTR names) + IDARG_IN_ADAPTER_INIT and calls IddCxAdapterInitAsync; EvtDeviceD0Entry triggers it (idempotent), EvtIddCxAdapterInitFinished stashes the adapter in a OnceLock for later DDIs. zeroed()+named-field construction dodges the Default-derive + field-order questions. Compiles + links clean on the box (pf_vdisplay.dll 268KB). CI gate = compile+link; the on-glass load/enumerate gate needs the box + an INF + SwDeviceCreate (next). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,76 @@
|
||||
//! 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::{iddcx, NTSTATUS, WDFDEVICE};
|
||||
|
||||
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;
|
||||
const 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();
|
||||
|
||||
/// 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;
|
||||
}
|
||||
|
||||
// Endpoint diagnostics (telemetry only — not used for OS runtime decisions). `pEndPointModelName`
|
||||
// must be a non-empty string; the rest are optional. GammaSupport stays NONE (zeroed).
|
||||
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.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");
|
||||
|
||||
let mut caps: iddcx::IDDCX_ADAPTER_CAPS = unsafe { core::mem::zeroed() };
|
||||
caps.Size = core::mem::size_of::<iddcx::IDDCX_ADAPTER_CAPS>() as u32;
|
||||
caps.Flags = iddcx::IDDCX_ADAPTER_FLAGS::IDDCX_ADAPTER_FLAGS_CAN_PROCESS_FP16;
|
||||
caps.MaxMonitorsSupported = 16;
|
||||
caps.EndPointDiagnostics = diag;
|
||||
|
||||
let init = iddcx::IDARG_IN_ADAPTER_INIT {
|
||||
WdfDevice: device,
|
||||
pCaps: &raw mut caps,
|
||||
ObjectAttributes: core::ptr::null_mut(),
|
||||
};
|
||||
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.
|
||||
unsafe { wdk_iddcx::IddCxAdapterInitAsync(&init, &mut out) }
|
||||
}
|
||||
|
||||
/// Stash the adapter object delivered by `EvtIddCxAdapterInitFinished` (STEP 4 reads it).
|
||||
pub fn set_adapter(adapter: iddcx::IDDCX_ADAPTER) {
|
||||
let _ = ADAPTER.set(SendAdapter(adapter));
|
||||
}
|
||||
@@ -11,20 +11,22 @@ use wdk_sys::{call_unsafe_wdf_function_binding, NTSTATUS, WDFDEVICE, WDFREQUEST}
|
||||
|
||||
use crate::{STATUS_NOT_IMPLEMENTED, STATUS_SUCCESS};
|
||||
|
||||
/// PnP `EvtDeviceD0Entry` (not an IddCx config callback). STEP 3 calls `DeviceContext::init_adapter`
|
||||
/// here (adapter creation is deferred to first D0, not driver_add).
|
||||
/// PnP `EvtDeviceD0Entry` (not an IddCx config callback). Adapter creation is deferred to the first D0
|
||||
/// (the adapter object is only valid after D0), not driver_add.
|
||||
pub unsafe extern "C" fn device_d0_entry(
|
||||
_device: WDFDEVICE,
|
||||
device: WDFDEVICE,
|
||||
_previous_state: wdk_sys::WDF_POWER_DEVICE_STATE,
|
||||
) -> NTSTATUS {
|
||||
STATUS_SUCCESS
|
||||
crate::adapter::init_adapter(device)
|
||||
}
|
||||
|
||||
/// Async completion of `IddCxAdapterInitAsync`. STEP 3: stash the adapter + start the watchdog.
|
||||
/// Async completion of `IddCxAdapterInitAsync`: stash the adapter for later DDIs. STEP 4 also starts the
|
||||
/// watchdog here.
|
||||
pub unsafe extern "C" fn adapter_init_finished(
|
||||
_adapter: iddcx::IDDCX_ADAPTER,
|
||||
adapter: iddcx::IDDCX_ADAPTER,
|
||||
_p_in: *const iddcx::IDARG_IN_ADAPTER_INIT_FINISHED,
|
||||
) -> NTSTATUS {
|
||||
crate::adapter::set_adapter(adapter);
|
||||
STATUS_SUCCESS
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
|
||||
#![allow(non_snake_case, clippy::missing_safety_doc)]
|
||||
|
||||
mod adapter;
|
||||
mod callbacks;
|
||||
#[allow(dead_code)] // salvaged verbatim; wired into the mode callbacks in STEP 4
|
||||
mod edid;
|
||||
|
||||
Reference in New Issue
Block a user