7bc204ffa0
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>
148 lines
7.4 KiB
Rust
148 lines
7.4 KiB
Rust
//! DriverEntry + driver_add — the IddCx device bring-up (STEP 2/3). wdk-build links the UMDF
|
|
//! `WdfDriverStubUm` whose `FxDriverEntryUm` forwards to the exported `DriverEntry`. Adapter creation is
|
|
//! deferred to the first `EvtDeviceD0Entry` (STEP 3); monitors are created on demand by the control
|
|
//! plane (STEP 4). Instrumented with `dbglog!` for on-glass bring-up.
|
|
|
|
use wdk_iddcx::nt_success;
|
|
use wdk_sys::{
|
|
GUID, NTSTATUS, PCUNICODE_STRING, PDRIVER_OBJECT, PWDFDEVICE_INIT, ULONG, WDF_DRIVER_CONFIG,
|
|
WDF_NO_HANDLE, WDF_NO_OBJECT_ATTRIBUTES, WDF_PNPPOWER_EVENT_CALLBACKS, WDFDEVICE, WDFDRIVER,
|
|
call_unsafe_wdf_function_binding, iddcx,
|
|
};
|
|
|
|
use crate::callbacks;
|
|
|
|
/// A WDF device context, attached to the WDFDEVICE at WdfDeviceCreate. The working virtual-display-rs +
|
|
/// oracle both create the device with a context-typed `DeviceContext` (we previously passed
|
|
/// WDF_NO_OBJECT_ATTRIBUTES). `WDF_OBJECT_CONTEXT_TYPE_INFO` holds raw pointers (Sync wrapper for the
|
|
/// `static`); `UniqueType` self-references per `WDF_DECLARE_CONTEXT_TYPE`.
|
|
#[repr(C)]
|
|
struct DeviceContext {
|
|
_device: WDFDEVICE,
|
|
}
|
|
#[repr(transparent)]
|
|
struct DevCtxInfo(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 DevCtxInfo {}
|
|
static DEVICE_CTX: DevCtxInfo = DevCtxInfo(wdk_sys::WDF_OBJECT_CONTEXT_TYPE_INFO {
|
|
Size: core::mem::size_of::<wdk_sys::WDF_OBJECT_CONTEXT_TYPE_INFO>() as u32,
|
|
ContextName: c"PfVdDeviceCtx".as_ptr().cast(),
|
|
ContextSize: core::mem::size_of::<DeviceContext>(),
|
|
UniqueType: &DEVICE_CTX.0,
|
|
EvtDriverGetUniqueContextType: None,
|
|
});
|
|
|
|
#[unsafe(export_name = "DriverEntry")]
|
|
pub unsafe extern "system" fn driver_entry(
|
|
driver: PDRIVER_OBJECT,
|
|
registry_path: PCUNICODE_STRING,
|
|
) -> NTSTATUS {
|
|
dbglog!("[pf-vd] DriverEntry");
|
|
// SAFETY: zeroed then Size + the device-add callback set, per the WDF_DRIVER_CONFIG contract.
|
|
let mut config: WDF_DRIVER_CONFIG = unsafe { core::mem::zeroed() };
|
|
config.Size = core::mem::size_of::<WDF_DRIVER_CONFIG>() as ULONG;
|
|
config.EvtDriverDeviceAdd = Some(driver_add);
|
|
// SAFETY: driver + registry_path are loader-provided; config is valid for the call.
|
|
let st = unsafe {
|
|
call_unsafe_wdf_function_binding!(
|
|
WdfDriverCreate,
|
|
driver,
|
|
registry_path,
|
|
WDF_NO_OBJECT_ATTRIBUTES,
|
|
&mut config,
|
|
WDF_NO_HANDLE.cast::<WDFDRIVER>()
|
|
)
|
|
};
|
|
dbglog!("[pf-vd] WdfDriverCreate -> {st:#x}");
|
|
st
|
|
}
|
|
|
|
extern "C" fn driver_add(_driver: WDFDRIVER, mut init: PWDFDEVICE_INIT) -> NTSTATUS {
|
|
dbglog!("[pf-vd] driver_add");
|
|
// Defer adapter creation to the first D0 entry.
|
|
let mut pnp: WDF_PNPPOWER_EVENT_CALLBACKS = unsafe { core::mem::zeroed() };
|
|
pnp.Size = core::mem::size_of::<WDF_PNPPOWER_EVENT_CALLBACKS>() as ULONG;
|
|
pnp.EvtDeviceD0Entry = Some(callbacks::device_d0_entry);
|
|
// SAFETY: init is the framework-provided device-init; pnp is valid for the call.
|
|
unsafe {
|
|
call_unsafe_wdf_function_binding!(WdfDeviceInitSetPnpPowerEventCallbacks, init, &mut pnp);
|
|
}
|
|
|
|
// Build the IddCx client config and wire the SDR callbacks. `.Size` = size_of (1.10 structs, 1.10 fw).
|
|
let mut cfg: iddcx::IDD_CX_CLIENT_CONFIG = unsafe { core::mem::zeroed() };
|
|
cfg.Size = core::mem::size_of::<iddcx::IDD_CX_CLIENT_CONFIG>() as u32;
|
|
cfg.EvtIddCxAdapterInitFinished = Some(callbacks::adapter_init_finished);
|
|
cfg.EvtIddCxParseMonitorDescription = Some(callbacks::parse_monitor_description);
|
|
cfg.EvtIddCxMonitorGetDefaultDescriptionModes = Some(callbacks::monitor_get_default_modes);
|
|
cfg.EvtIddCxMonitorQueryTargetModes = Some(callbacks::monitor_query_modes);
|
|
cfg.EvtIddCxAdapterCommitModes = Some(callbacks::adapter_commit_modes);
|
|
// STEP 7 (HDR): the *2 mode DDIs + the gamma/HDR-metadata/query-target-info callbacks. The adapter
|
|
// caps now set CAN_PROCESS_FP16 (adapter.rs), which OBLIGATES this whole set — without them the OS
|
|
// rejects the adapter at init ("Failed to get adapter"). The proven oracle (entry.rs) registers the *2
|
|
// variants ALONGSIDE the v1 callbacks above (NOT instead of them) — the OS prefers the *2 on IddCx
|
|
// 1.10 and falls back to v1 down-level — so we replicate exactly: keep both. The framework no longer
|
|
// rejects the *2 set because the FP16 cap is now present (the only reason STEP 3 had to drop them).
|
|
cfg.EvtIddCxParseMonitorDescription2 = Some(callbacks::parse_monitor_description2);
|
|
cfg.EvtIddCxMonitorQueryTargetModes2 = Some(callbacks::monitor_query_modes2);
|
|
cfg.EvtIddCxAdapterCommitModes2 = Some(callbacks::adapter_commit_modes2);
|
|
cfg.EvtIddCxAdapterQueryTargetInfo = Some(callbacks::query_target_info);
|
|
cfg.EvtIddCxMonitorSetDefaultHdrMetaData = Some(callbacks::set_default_hdr_metadata);
|
|
cfg.EvtIddCxMonitorSetGammaRamp = Some(callbacks::set_gamma_ramp);
|
|
cfg.EvtIddCxMonitorAssignSwapChain = Some(callbacks::assign_swap_chain);
|
|
cfg.EvtIddCxMonitorUnassignSwapChain = Some(callbacks::unassign_swap_chain);
|
|
cfg.EvtIddCxDeviceIoControl = Some(callbacks::device_io_control);
|
|
|
|
// SAFETY: init is the framework device-init; cfg is fully populated + sized. (Links IddCxStub.)
|
|
let status = unsafe { wdk_iddcx::IddCxDeviceInitConfig(init, &cfg) };
|
|
dbglog!("[pf-vd] IddCxDeviceInitConfig -> {status:#x}");
|
|
if !nt_success(status) {
|
|
return status;
|
|
}
|
|
|
|
let mut device: WDFDEVICE = core::ptr::null_mut();
|
|
// Attach a device context type (like the working virtual-display-rs/oracle), not WDF_NO_OBJECT_ATTRIBUTES.
|
|
let mut dev_attr: wdk_sys::WDF_OBJECT_ATTRIBUTES = unsafe { core::mem::zeroed() };
|
|
dev_attr.Size = core::mem::size_of::<wdk_sys::WDF_OBJECT_ATTRIBUTES>() as u32;
|
|
dev_attr.ExecutionLevel = wdk_sys::_WDF_EXECUTION_LEVEL::WdfExecutionLevelInheritFromParent;
|
|
dev_attr.SynchronizationScope =
|
|
wdk_sys::_WDF_SYNCHRONIZATION_SCOPE::WdfSynchronizationScopeInheritFromParent;
|
|
dev_attr.ContextTypeInfo = &DEVICE_CTX.0;
|
|
// SAFETY: init configured above; dev_attr is a valid context-typed attributes block.
|
|
let status = unsafe {
|
|
call_unsafe_wdf_function_binding!(WdfDeviceCreate, &mut init, &mut dev_attr, &mut device)
|
|
};
|
|
dbglog!("[pf-vd] WdfDeviceCreate -> {status:#x}");
|
|
if !nt_success(status) {
|
|
return status;
|
|
}
|
|
|
|
// SAFETY: device is the just-created WDFDEVICE.
|
|
let status = unsafe { wdk_iddcx::IddCxDeviceInitialize(device) };
|
|
dbglog!("[pf-vd] IddCxDeviceInitialize -> {status:#x}");
|
|
if !nt_success(status) {
|
|
return status;
|
|
}
|
|
|
|
// Expose the owned pf-vdisplay control interface: the host opens this GUID and drives the proto control
|
|
// plane (IOCTL_ADD/REMOVE/PING/…) which arrives at EvtIddCxDeviceIoControl. NOT SudoVDA's GUID. (The
|
|
// upstream uses a socket instead, so it has no interface; ours is IOCTL-based.)
|
|
let (d1, d2, d3, d4) = pf_vdisplay_proto::interface_guid_fields();
|
|
let guid = GUID {
|
|
Data1: d1,
|
|
Data2: d2,
|
|
Data3: d3,
|
|
Data4: d4,
|
|
};
|
|
// SAFETY: device is the just-created WDFDEVICE; guid lives for the call; no reference string.
|
|
let status = unsafe {
|
|
call_unsafe_wdf_function_binding!(
|
|
WdfDeviceCreateDeviceInterface,
|
|
device,
|
|
&guid,
|
|
core::ptr::null()
|
|
)
|
|
};
|
|
dbglog!("[pf-vd] WdfDeviceCreateDeviceInterface -> {status:#x}");
|
|
status
|
|
}
|