refactor(windows-drivers): STEP 8 (1/n) — unsafe-reduction pass (per-site // SAFETY)
windows-drivers / probe-and-proto (push) Successful in 19s
apple / swift (push) Successful in 1m7s
ci / rust (push) Successful in 1m14s
windows-drivers / driver-build (push) Successful in 1m8s
ci / web (push) Successful in 40s
ci / docs-site (push) Successful in 1m1s
android / android (push) Successful in 3m13s
apple / screenshots (push) Successful in 3m14s
deb / build-publish (push) Successful in 2m38s
decky / build-publish (push) Successful in 12s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 4s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 5s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 4s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 5s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 4s
windows-host / package (push) Successful in 5m18s
ci / bench (push) Successful in 4m35s
rpm / build-publish (bazzite, punktfunk-fedora-rpm) (push) Successful in 8m26s
rpm / build-publish (fedora-44, punktfunk-fedora44-rpm) (push) Successful in 8m16s
docker / deploy-docs (push) Successful in 31s
windows-drivers / probe-and-proto (push) Successful in 19s
apple / swift (push) Successful in 1m7s
ci / rust (push) Successful in 1m14s
windows-drivers / driver-build (push) Successful in 1m8s
ci / web (push) Successful in 40s
ci / docs-site (push) Successful in 1m1s
android / android (push) Successful in 3m13s
apple / screenshots (push) Successful in 3m14s
deb / build-publish (push) Successful in 2m38s
decky / build-publish (push) Successful in 12s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 4s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 5s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 4s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 5s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 4s
windows-host / package (push) Successful in 5m18s
ci / bench (push) Successful in 4m35s
rpm / build-publish (bazzite, punktfunk-fedora-rpm) (push) Successful in 8m26s
rpm / build-publish (fedora-44, punktfunk-fedora44-rpm) (push) Successful in 8m16s
docker / deploy-docs (push) Successful in 31s
Audit pass over the new pf-vdisplay driver's unsafe surface: 92 per-site // SAFETY comments added across adapter.rs / monitor.rs / entry.rs / callbacks.rs / swap_chain_processor.rs / frame_transport.rs / direct_3d_device.rs (control.rs already had full coverage). COMMENTS ONLY — zero logic, signature, or control-flow change (verified via git diff: every added line is a // SAFETY comment or blank). The dominant gap was the pervasive `core::mem::zeroed()` FFI-struct builds (IDDCX_*/WDF_*/ DISPLAYCONFIG_* C PODs whose all-zero bit pattern is a valid uninitialized/Invalid state, with the required .Size/fields set immediately after) — each now carries a one-line // SAFETY. Plus explicit notes on the two stack/local-pointer-into-FFI hazards (adapter.rs `version` ptr into IddCxAdapterInitAsync; monitor.rs `edid` Vec ptr into IddCxMonitorCreate — both read synchronously before the local drops) and the frame_transport.rs raw-HANDLE / mapped-header derefs + cleanup paths. The already-justified Send/Sync wrappers (SendAdapter, CtxTypeInfo/DevCtxInfo, MonitorObject, Sendable, FramePublisher) were audited — each already carried a // SAFETY. No site needed a code change. First slice of STEP 8 (the SudoVDA drop). Comments-only ⇒ build-neutral; windows-drivers.yml verifies on the next runner build. Remaining STEP 8: re-vendor the installer's driver binary from the new drivers/ tree (the shipping packaging/windows/pf-vdisplay/ binary is still built from the OLD oracle tree with the SudoVDA-compat GUID — ABI-mismatched with the host's proto GUID), add an .inx to the new tree, re-point scripts/README from vdisplay-driver/ to drivers/, flip the selector default to pf-vdisplay, then delete the old oracle tree. Keep sudovda.rs (the runtime fallback + the backend-neutral CCD helpers pf_vdisplay.rs reuses) and the WGC-relay/DDA secure path (the secure-desktop gate is not yet passed on glass). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -66,6 +66,8 @@ pub fn init_adapter(device: WDFDEVICE) -> NTSTATUS {
|
|||||||
// Firmware/hardware version (telemetry). The oracle points BOTH at one IDDCX_ENDPOINT_VERSION.
|
// 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`
|
// `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).
|
// is `size_of` throughout — these are the IddCx 1.10 structs and the framework here is 1.10 (= upstream).
|
||||||
|
// SAFETY: building a C POD — the all-zero bit pattern is a valid uninitialized IDDCX_ENDPOINT_VERSION;
|
||||||
|
// the required `.Size` (+ version fields) are set immediately below before the struct is used.
|
||||||
let mut version: iddcx::IDDCX_ENDPOINT_VERSION = unsafe { core::mem::zeroed() };
|
let mut version: iddcx::IDDCX_ENDPOINT_VERSION = unsafe { core::mem::zeroed() };
|
||||||
version.Size = core::mem::size_of::<iddcx::IDDCX_ENDPOINT_VERSION>() as u32;
|
version.Size = core::mem::size_of::<iddcx::IDDCX_ENDPOINT_VERSION>() as u32;
|
||||||
version.MajorVer = env!("CARGO_PKG_VERSION_MAJOR").parse().unwrap_or(0);
|
version.MajorVer = env!("CARGO_PKG_VERSION_MAJOR").parse().unwrap_or(0);
|
||||||
@@ -76,6 +78,8 @@ pub fn init_adapter(device: WDFDEVICE) -> NTSTATUS {
|
|||||||
// zeroed value is IDDCX_FEATURE_IMPLEMENTATION_UNINITIALIZED (0), which the framework's adapter Validate
|
// 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
|
// rejects with INVALID_PARAMETER (ddivalidation.cpp:797) — set it to NONE (1) like upstream. THIS was
|
||||||
// the on-glass adapter-init blocker.
|
// the on-glass adapter-init blocker.
|
||||||
|
// SAFETY: building a C POD — the all-zero bit pattern is a valid uninitialized
|
||||||
|
// IDDCX_ENDPOINT_DIAGNOSTIC_INFO; the required `.Size` (+ the fields read by Validate) are set below.
|
||||||
let mut diag: iddcx::IDDCX_ENDPOINT_DIAGNOSTIC_INFO = unsafe { core::mem::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.Size = core::mem::size_of::<iddcx::IDDCX_ENDPOINT_DIAGNOSTIC_INFO>() as u32;
|
||||||
diag.GammaSupport = iddcx::IDDCX_FEATURE_IMPLEMENTATION::IDDCX_FEATURE_IMPLEMENTATION_NONE;
|
diag.GammaSupport = iddcx::IDDCX_FEATURE_IMPLEMENTATION::IDDCX_FEATURE_IMPLEMENTATION_NONE;
|
||||||
@@ -83,9 +87,13 @@ pub fn init_adapter(device: WDFDEVICE) -> NTSTATUS {
|
|||||||
diag.pEndPointFriendlyName = wstr!("punktfunk Virtual Display Adapter");
|
diag.pEndPointFriendlyName = wstr!("punktfunk Virtual Display Adapter");
|
||||||
diag.pEndPointManufacturerName = wstr!("punktfunk");
|
diag.pEndPointManufacturerName = wstr!("punktfunk");
|
||||||
diag.pEndPointModelName = wstr!("Virtual Display");
|
diag.pEndPointModelName = wstr!("Virtual Display");
|
||||||
|
// SAFETY: `version` is a stack local that outlives this `init_adapter` call; IddCxAdapterInitAsync
|
||||||
|
// (below) reads through these pointers SYNCHRONOUSLY, before `version` drops — the pointer never escapes.
|
||||||
diag.pFirmwareVersion = (&raw mut version).cast();
|
diag.pFirmwareVersion = (&raw mut version).cast();
|
||||||
diag.pHardwareVersion = (&raw mut version).cast();
|
diag.pHardwareVersion = (&raw mut version).cast();
|
||||||
|
|
||||||
|
// SAFETY: building a C POD — the all-zero bit pattern is a valid uninitialized IDDCX_ADAPTER_CAPS;
|
||||||
|
// the required `.Size` (+ flags/limits/diag) are set immediately below.
|
||||||
let mut caps: iddcx::IDDCX_ADAPTER_CAPS = unsafe { core::mem::zeroed() };
|
let mut caps: iddcx::IDDCX_ADAPTER_CAPS = unsafe { core::mem::zeroed() };
|
||||||
caps.Size = core::mem::size_of::<iddcx::IDDCX_ADAPTER_CAPS>() as u32;
|
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
|
// STEP 7 (HDR): declare we can process FP16 (scRGB) desktop surfaces — this is what marks the virtual
|
||||||
@@ -101,6 +109,8 @@ pub fn init_adapter(device: WDFDEVICE) -> NTSTATUS {
|
|||||||
|
|
||||||
// The adapter WDF object's attributes: Size + Synchronization/Execution = InheritFromParent (NOT zeroed,
|
// 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).
|
// since zero = *Invalid*) + the adapter context type (STEP 4 stores adapter state here).
|
||||||
|
// SAFETY: building a C POD — the all-zero bit pattern is a valid uninitialized WDF_OBJECT_ATTRIBUTES;
|
||||||
|
// the required `.Size` (+ execution/sync scope + context type) are set immediately below.
|
||||||
let mut attr: wdk_sys::WDF_OBJECT_ATTRIBUTES = unsafe { core::mem::zeroed() };
|
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.Size = core::mem::size_of::<wdk_sys::WDF_OBJECT_ATTRIBUTES>() as u32;
|
||||||
attr.ExecutionLevel = wdk_sys::_WDF_EXECUTION_LEVEL::WdfExecutionLevelInheritFromParent;
|
attr.ExecutionLevel = wdk_sys::_WDF_EXECUTION_LEVEL::WdfExecutionLevelInheritFromParent;
|
||||||
@@ -112,6 +122,8 @@ pub fn init_adapter(device: WDFDEVICE) -> NTSTATUS {
|
|||||||
pCaps: &raw mut caps,
|
pCaps: &raw mut caps,
|
||||||
ObjectAttributes: &raw mut attr,
|
ObjectAttributes: &raw mut attr,
|
||||||
};
|
};
|
||||||
|
// SAFETY: building a C POD — the all-zero bit pattern is a valid uninitialized IDARG_OUT_ADAPTER_INIT
|
||||||
|
// (an out-param the framework fills).
|
||||||
let mut out: iddcx::IDARG_OUT_ADAPTER_INIT = unsafe { core::mem::zeroed() };
|
let mut out: iddcx::IDARG_OUT_ADAPTER_INIT = unsafe { core::mem::zeroed() };
|
||||||
// SAFETY: `init`/`out` are valid local storage; IddCxAdapterInitAsync reads the caps synchronously
|
// 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.
|
// (the adapter object itself is delivered later via adapter_init_finished). Called once per device.
|
||||||
|
|||||||
@@ -70,6 +70,8 @@ pub unsafe extern "C" fn parse_monitor_description(
|
|||||||
// SAFETY: `pMonitorModes` points to >= `count` IDDCX_MONITOR_MODE entries (validated above).
|
// SAFETY: `pMonitorModes` points to >= `count` IDDCX_MONITOR_MODE entries (validated above).
|
||||||
let out = unsafe { core::slice::from_raw_parts_mut(in_args.pMonitorModes, count as usize) };
|
let out = unsafe { core::slice::from_raw_parts_mut(in_args.pMonitorModes, count as usize) };
|
||||||
for (item, slot) in crate::monitor::flatten(&modes).zip(out.iter_mut()) {
|
for (item, slot) in crate::monitor::flatten(&modes).zip(out.iter_mut()) {
|
||||||
|
// SAFETY: building a C POD — the all-zero bit pattern is a valid uninitialized IDDCX_MONITOR_MODE;
|
||||||
|
// the required `.Size` (+ origin / signal info) are set immediately below.
|
||||||
let mut mode: iddcx::IDDCX_MONITOR_MODE = unsafe { core::mem::zeroed() };
|
let mut mode: iddcx::IDDCX_MONITOR_MODE = unsafe { core::mem::zeroed() };
|
||||||
mode.Size = core::mem::size_of::<iddcx::IDDCX_MONITOR_MODE>() as u32;
|
mode.Size = core::mem::size_of::<iddcx::IDDCX_MONITOR_MODE>() as u32;
|
||||||
mode.Origin = iddcx::IDDCX_MONITOR_MODE_ORIGIN::IDDCX_MONITOR_MODE_ORIGIN_MONITORDESCRIPTOR;
|
mode.Origin = iddcx::IDDCX_MONITOR_MODE_ORIGIN::IDDCX_MONITOR_MODE_ORIGIN_MONITORDESCRIPTOR;
|
||||||
@@ -119,6 +121,8 @@ pub unsafe extern "C" fn parse_monitor_description2(
|
|||||||
// SAFETY: `pMonitorModes` points to >= `count` IDDCX_MONITOR_MODE2 entries (validated above).
|
// SAFETY: `pMonitorModes` points to >= `count` IDDCX_MONITOR_MODE2 entries (validated above).
|
||||||
let out = unsafe { core::slice::from_raw_parts_mut(in_args.pMonitorModes, count as usize) };
|
let out = unsafe { core::slice::from_raw_parts_mut(in_args.pMonitorModes, count as usize) };
|
||||||
for (item, slot) in crate::monitor::flatten(&modes).zip(out.iter_mut()) {
|
for (item, slot) in crate::monitor::flatten(&modes).zip(out.iter_mut()) {
|
||||||
|
// SAFETY: building a C POD — the all-zero bit pattern is a valid uninitialized IDDCX_MONITOR_MODE2;
|
||||||
|
// the required `.Size` (+ origin / signal info / bit depth) are set immediately below.
|
||||||
let mut mode: iddcx::IDDCX_MONITOR_MODE2 = unsafe { core::mem::zeroed() };
|
let mut mode: iddcx::IDDCX_MONITOR_MODE2 = unsafe { core::mem::zeroed() };
|
||||||
mode.Size = core::mem::size_of::<iddcx::IDDCX_MONITOR_MODE2>() as u32;
|
mode.Size = core::mem::size_of::<iddcx::IDDCX_MONITOR_MODE2>() as u32;
|
||||||
mode.Origin = iddcx::IDDCX_MONITOR_MODE_ORIGIN::IDDCX_MONITOR_MODE_ORIGIN_MONITORDESCRIPTOR;
|
mode.Origin = iddcx::IDDCX_MONITOR_MODE_ORIGIN::IDDCX_MONITOR_MODE_ORIGIN_MONITORDESCRIPTOR;
|
||||||
|
|||||||
@@ -60,14 +60,18 @@ pub struct Direct3DDevice {
|
|||||||
|
|
||||||
impl Direct3DDevice {
|
impl Direct3DDevice {
|
||||||
pub fn init(adapter_luid: LUID) -> Result<Self, Direct3DError> {
|
pub fn init(adapter_luid: LUID) -> Result<Self, Direct3DError> {
|
||||||
|
// SAFETY: a plain DXGI factory-creation call; `?` returns the error on failure.
|
||||||
let dxgi_factory =
|
let dxgi_factory =
|
||||||
unsafe { CreateDXGIFactory2::<IDXGIFactory5>(DXGI_CREATE_FACTORY_FLAGS(0))? };
|
unsafe { CreateDXGIFactory2::<IDXGIFactory5>(DXGI_CREATE_FACTORY_FLAGS(0))? };
|
||||||
|
|
||||||
|
// SAFETY: `dxgi_factory` is the live factory just created; `adapter_luid` is a by-value LUID.
|
||||||
let adapter = unsafe { dxgi_factory.EnumAdapterByLuid::<IDXGIAdapter1>(adapter_luid)? };
|
let adapter = unsafe { dxgi_factory.EnumAdapterByLuid::<IDXGIAdapter1>(adapter_luid)? };
|
||||||
|
|
||||||
let mut device = None;
|
let mut device = None;
|
||||||
let mut device_context = None;
|
let mut device_context = None;
|
||||||
|
|
||||||
|
// SAFETY: `adapter` is a live IDXGIAdapter1; `device`/`device_context` are valid local out-params
|
||||||
|
// (checked for None below); the flag set + SDK version are valid constants. `?` returns on failure.
|
||||||
unsafe {
|
unsafe {
|
||||||
D3D11CreateDevice(
|
D3D11CreateDevice(
|
||||||
&adapter,
|
&adapter,
|
||||||
|
|||||||
@@ -60,6 +60,8 @@ pub unsafe extern "system" fn driver_entry(
|
|||||||
extern "C" fn driver_add(_driver: WDFDRIVER, mut init: PWDFDEVICE_INIT) -> NTSTATUS {
|
extern "C" fn driver_add(_driver: WDFDRIVER, mut init: PWDFDEVICE_INIT) -> NTSTATUS {
|
||||||
dbglog!("[pf-vd] driver_add");
|
dbglog!("[pf-vd] driver_add");
|
||||||
// Defer adapter creation to the first D0 entry.
|
// Defer adapter creation to the first D0 entry.
|
||||||
|
// SAFETY: building a C POD — the all-zero bit pattern is a valid uninitialized
|
||||||
|
// WDF_PNPPOWER_EVENT_CALLBACKS; the required `.Size` (+ the D0-entry callback) are set immediately below.
|
||||||
let mut pnp: WDF_PNPPOWER_EVENT_CALLBACKS = unsafe { core::mem::zeroed() };
|
let mut pnp: WDF_PNPPOWER_EVENT_CALLBACKS = unsafe { core::mem::zeroed() };
|
||||||
pnp.Size = core::mem::size_of::<WDF_PNPPOWER_EVENT_CALLBACKS>() as ULONG;
|
pnp.Size = core::mem::size_of::<WDF_PNPPOWER_EVENT_CALLBACKS>() as ULONG;
|
||||||
pnp.EvtDeviceD0Entry = Some(callbacks::device_d0_entry);
|
pnp.EvtDeviceD0Entry = Some(callbacks::device_d0_entry);
|
||||||
@@ -69,6 +71,8 @@ extern "C" fn driver_add(_driver: WDFDRIVER, mut init: PWDFDEVICE_INIT) -> NTSTA
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Build the IddCx client config and wire the SDR callbacks. `.Size` = size_of (1.10 structs, 1.10 fw).
|
// Build the IddCx client config and wire the SDR callbacks. `.Size` = size_of (1.10 structs, 1.10 fw).
|
||||||
|
// SAFETY: building a C POD — the all-zero bit pattern is a valid uninitialized IDD_CX_CLIENT_CONFIG;
|
||||||
|
// the required `.Size` (+ the IddCx client callbacks) are set immediately below.
|
||||||
let mut cfg: iddcx::IDD_CX_CLIENT_CONFIG = unsafe { core::mem::zeroed() };
|
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.Size = core::mem::size_of::<iddcx::IDD_CX_CLIENT_CONFIG>() as u32;
|
||||||
cfg.EvtIddCxAdapterInitFinished = Some(callbacks::adapter_init_finished);
|
cfg.EvtIddCxAdapterInitFinished = Some(callbacks::adapter_init_finished);
|
||||||
@@ -101,6 +105,8 @@ extern "C" fn driver_add(_driver: WDFDRIVER, mut init: PWDFDEVICE_INIT) -> NTSTA
|
|||||||
|
|
||||||
let mut device: WDFDEVICE = core::ptr::null_mut();
|
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.
|
// Attach a device context type (like the working virtual-display-rs/oracle), not WDF_NO_OBJECT_ATTRIBUTES.
|
||||||
|
// SAFETY: building a C POD — the all-zero bit pattern is a valid uninitialized WDF_OBJECT_ATTRIBUTES;
|
||||||
|
// the required `.Size` (+ execution/sync scope + context type) are set immediately below.
|
||||||
let mut dev_attr: wdk_sys::WDF_OBJECT_ATTRIBUTES = unsafe { core::mem::zeroed() };
|
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.Size = core::mem::size_of::<wdk_sys::WDF_OBJECT_ATTRIBUTES>() as u32;
|
||||||
dev_attr.ExecutionLevel = wdk_sys::_WDF_EXECUTION_LEVEL::WdfExecutionLevelInheritFromParent;
|
dev_attr.ExecutionLevel = wdk_sys::_WDF_EXECUTION_LEVEL::WdfExecutionLevelInheritFromParent;
|
||||||
|
|||||||
@@ -90,6 +90,7 @@ impl FramePublisher {
|
|||||||
context: &ID3D11DeviceContext,
|
context: &ID3D11DeviceContext,
|
||||||
) -> windows::core::Result<Self> {
|
) -> windows::core::Result<Self> {
|
||||||
// 1. Open the host-created header (RW). Err if the host hasn't created it yet.
|
// 1. Open the host-created header (RW). Err if the host hasn't created it yet.
|
||||||
|
// SAFETY: a plain Win32 call; the name HSTRING is valid for the call (`?` returns on failure).
|
||||||
let map = unsafe {
|
let map = unsafe {
|
||||||
OpenFileMappingW(
|
OpenFileMappingW(
|
||||||
FILE_MAP_ALL_ACCESS.0,
|
FILE_MAP_ALL_ACCESS.0,
|
||||||
@@ -97,6 +98,8 @@ impl FramePublisher {
|
|||||||
&HSTRING::from(header_name(target_id)),
|
&HSTRING::from(header_name(target_id)),
|
||||||
)?
|
)?
|
||||||
};
|
};
|
||||||
|
// SAFETY: `map` is the just-opened file mapping; mapping size_of::<SharedHeader>() bytes of it
|
||||||
|
// (the host created the mapping at >= that size). The null `view.Value` is checked below.
|
||||||
let view = unsafe {
|
let view = unsafe {
|
||||||
MapViewOfFile(
|
MapViewOfFile(
|
||||||
map,
|
map,
|
||||||
@@ -107,6 +110,7 @@ impl FramePublisher {
|
|||||||
)
|
)
|
||||||
};
|
};
|
||||||
if view.Value.is_null() {
|
if view.Value.is_null() {
|
||||||
|
// SAFETY: `map` is the just-opened mapping handle, closed once here on the error path.
|
||||||
unsafe {
|
unsafe {
|
||||||
let _ = CloseHandle(map);
|
let _ = CloseHandle(map);
|
||||||
}
|
}
|
||||||
@@ -115,16 +119,21 @@ impl FramePublisher {
|
|||||||
let header = view.Value.cast::<SharedHeader>();
|
let header = view.Value.cast::<SharedHeader>();
|
||||||
|
|
||||||
// 2. Report our render adapter to the host immediately (lets it detect a mismatch).
|
// 2. Report our render adapter to the host immediately (lets it detect a mismatch).
|
||||||
|
// SAFETY: `header` points to the mapped, non-null host header (>= size_of::<SharedHeader>() bytes);
|
||||||
|
// these scalar writes are within it. The host opened the section with a permissive SDDL for us.
|
||||||
unsafe {
|
unsafe {
|
||||||
(*header).driver_render_luid_low = render_luid_low;
|
(*header).driver_render_luid_low = render_luid_low;
|
||||||
(*header).driver_render_luid_high = render_luid_high;
|
(*header).driver_render_luid_high = render_luid_high;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. The host sets magic==MAGIC only once the ring textures exist. Not ready → retry later.
|
// 3. The host sets magic==MAGIC only once the ring textures exist. Not ready → retry later.
|
||||||
|
// SAFETY: `header` is the mapped host header; `magic` lives within it and is read atomically
|
||||||
|
// (Acquire) to pair with the host's Release store once the ring textures are published.
|
||||||
let magic = unsafe {
|
let magic = unsafe {
|
||||||
(*(core::ptr::addr_of!((*header).magic) as *const AtomicU32)).load(Ordering::Acquire)
|
(*(core::ptr::addr_of!((*header).magic) as *const AtomicU32)).load(Ordering::Acquire)
|
||||||
};
|
};
|
||||||
if magic != MAGIC {
|
if magic != MAGIC {
|
||||||
|
// SAFETY: `header`/`map` are the live mapped view + handle; unmapped + closed once on this path.
|
||||||
unsafe {
|
unsafe {
|
||||||
let _ = UnmapViewOfFile(MEMORY_MAPPED_VIEW_ADDRESS {
|
let _ = UnmapViewOfFile(MEMORY_MAPPED_VIEW_ADDRESS {
|
||||||
Value: header.cast(),
|
Value: header.cast(),
|
||||||
@@ -133,10 +142,12 @@ impl FramePublisher {
|
|||||||
}
|
}
|
||||||
return Err(windows::core::Error::from_win32());
|
return Err(windows::core::Error::from_win32());
|
||||||
}
|
}
|
||||||
|
// SAFETY: `header` is the mapped host header; these scalar fields live within it.
|
||||||
let (generation, ring_len) =
|
let (generation, ring_len) =
|
||||||
unsafe { ((*header).generation, (*header).ring_len.min(RING_LEN)) };
|
unsafe { ((*header).generation, (*header).ring_len.min(RING_LEN)) };
|
||||||
|
|
||||||
// 4. Open the event (SYNCHRONIZE | EVENT_MODIFY_STATE so we can SetEvent).
|
// 4. Open the event (SYNCHRONIZE | EVENT_MODIFY_STATE so we can SetEvent).
|
||||||
|
// SAFETY: a plain Win32 call; the name HSTRING is valid for the call.
|
||||||
let event = match unsafe {
|
let event = match unsafe {
|
||||||
OpenEventW(
|
OpenEventW(
|
||||||
SYNCHRONIZATION_ACCESS_RIGHTS(EVENT_ACCESS),
|
SYNCHRONIZATION_ACCESS_RIGHTS(EVENT_ACCESS),
|
||||||
@@ -146,6 +157,7 @@ impl FramePublisher {
|
|||||||
} {
|
} {
|
||||||
Ok(e) => e,
|
Ok(e) => e,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
|
// SAFETY: `header`/`map` are the live mapped view + handle; unmapped + closed once here.
|
||||||
unsafe {
|
unsafe {
|
||||||
let _ = UnmapViewOfFile(MEMORY_MAPPED_VIEW_ADDRESS {
|
let _ = UnmapViewOfFile(MEMORY_MAPPED_VIEW_ADDRESS {
|
||||||
Value: header.cast(),
|
Value: header.cast(),
|
||||||
@@ -160,6 +172,8 @@ impl FramePublisher {
|
|||||||
let device1: ID3D11Device1 = match device.cast() {
|
let device1: ID3D11Device1 = match device.cast() {
|
||||||
Ok(d) => d,
|
Ok(d) => d,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
|
// SAFETY: `header` is the mapped host header (status write within it); `event`/`map` are the
|
||||||
|
// live handles, all released once on this error path.
|
||||||
unsafe {
|
unsafe {
|
||||||
(*header).driver_status = DRV_STATUS_NO_DEVICE1;
|
(*header).driver_status = DRV_STATUS_NO_DEVICE1;
|
||||||
let _ = CloseHandle(event);
|
let _ = CloseHandle(event);
|
||||||
@@ -174,12 +188,15 @@ impl FramePublisher {
|
|||||||
let mut slots = Vec::new();
|
let mut slots = Vec::new();
|
||||||
for k in 0..ring_len {
|
for k in 0..ring_len {
|
||||||
let name = HSTRING::from(texture_name(target_id, generation, k));
|
let name = HSTRING::from(texture_name(target_id, generation, k));
|
||||||
|
// SAFETY: `device1` is a live ID3D11Device1; the name HSTRING is valid for the call.
|
||||||
let opened: windows::core::Result<ID3D11Texture2D> =
|
let opened: windows::core::Result<ID3D11Texture2D> =
|
||||||
unsafe { device1.OpenSharedResourceByName(&name, DXGI_SHARED_RESOURCE_RW) };
|
unsafe { device1.OpenSharedResourceByName(&name, DXGI_SHARED_RESOURCE_RW) };
|
||||||
match opened {
|
match opened {
|
||||||
Ok(tex) => match tex.cast::<IDXGIKeyedMutex>() {
|
Ok(tex) => match tex.cast::<IDXGIKeyedMutex>() {
|
||||||
Ok(mutex) => slots.push(Slot { tex, mutex }),
|
Ok(mutex) => slots.push(Slot { tex, mutex }),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
|
// SAFETY: `header` is the mapped host header (status writes within it); `event`/`map`
|
||||||
|
// are the live handles, all released once on this error path.
|
||||||
unsafe {
|
unsafe {
|
||||||
(*header).driver_status = DRV_STATUS_TEX_FAIL;
|
(*header).driver_status = DRV_STATUS_TEX_FAIL;
|
||||||
(*header).driver_status_detail = e.code().0 as u32;
|
(*header).driver_status_detail = e.code().0 as u32;
|
||||||
@@ -195,6 +212,8 @@ impl FramePublisher {
|
|||||||
Err(e) => {
|
Err(e) => {
|
||||||
// Most likely a render-adapter mismatch (the host made the textures on a different
|
// Most likely a render-adapter mismatch (the host made the textures on a different
|
||||||
// GPU than the swap-chain renders on). Tell the host so it can report it.
|
// GPU than the swap-chain renders on). Tell the host so it can report it.
|
||||||
|
// SAFETY: `header` is the mapped host header (status writes within it); `event`/`map`
|
||||||
|
// are the live handles, all released once on this error path.
|
||||||
unsafe {
|
unsafe {
|
||||||
(*header).driver_status = DRV_STATUS_TEX_FAIL;
|
(*header).driver_status = DRV_STATUS_TEX_FAIL;
|
||||||
(*header).driver_status_detail = e.code().0 as u32;
|
(*header).driver_status_detail = e.code().0 as u32;
|
||||||
@@ -209,6 +228,7 @@ impl FramePublisher {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SAFETY: `header` is the mapped host header; the status field lives within it.
|
||||||
unsafe {
|
unsafe {
|
||||||
(*header).driver_status = DRV_STATUS_OPENED;
|
(*header).driver_status = DRV_STATUS_OPENED;
|
||||||
}
|
}
|
||||||
@@ -223,6 +243,7 @@ impl FramePublisher {
|
|||||||
slots,
|
slots,
|
||||||
next: 0,
|
next: 0,
|
||||||
seq: 0,
|
seq: 0,
|
||||||
|
// SAFETY: `header` is the mapped host header; `dxgi_format` lives within it.
|
||||||
ring_format: unsafe { (*header).dxgi_format },
|
ring_format: unsafe { (*header).dxgi_format },
|
||||||
generation,
|
generation,
|
||||||
})
|
})
|
||||||
@@ -230,6 +251,8 @@ impl FramePublisher {
|
|||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn latest_cell(&self) -> &AtomicU64 {
|
fn latest_cell(&self) -> &AtomicU64 {
|
||||||
|
// SAFETY: `self.header` stays mapped for the publisher's lifetime (unmapped only in Drop); the
|
||||||
|
// `latest` field lives within it and is naturally aligned, so this AtomicU64 reference is valid.
|
||||||
unsafe { &*(core::ptr::addr_of!((*self.header).latest) as *const AtomicU64) }
|
unsafe { &*(core::ptr::addr_of!((*self.header).latest) as *const AtomicU64) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -237,6 +260,8 @@ impl FramePublisher {
|
|||||||
/// mode flipped, so the ring format changed (FP16 ⇄ BGRA) and the texture names now carry a new
|
/// mode flipped, so the ring format changed (FP16 ⇄ BGRA) and the texture names now carry a new
|
||||||
/// generation. `run_core` drops the publisher on this so it re-attaches to the new ring.
|
/// generation. `run_core` drops the publisher on this so it re-attaches to the new ring.
|
||||||
pub fn is_stale(&self) -> bool {
|
pub fn is_stale(&self) -> bool {
|
||||||
|
// SAFETY: `self.header` stays mapped for the publisher's lifetime; `generation` lives within it and
|
||||||
|
// is read atomically (Acquire) to pair with the host's Release bump on a mid-session ring recreate.
|
||||||
let cur = unsafe {
|
let cur = unsafe {
|
||||||
(*(core::ptr::addr_of!((*self.header).generation) as *const AtomicU32))
|
(*(core::ptr::addr_of!((*self.header).generation) as *const AtomicU32))
|
||||||
.load(Ordering::Acquire)
|
.load(Ordering::Acquire)
|
||||||
@@ -254,6 +279,7 @@ impl FramePublisher {
|
|||||||
// frame that doesn't match (e.g. an FP16 HDR surface arriving while the ring is still BGRA, before
|
// frame that doesn't match (e.g. an FP16 HDR surface arriving while the ring is still BGRA, before
|
||||||
// the host recreates the ring as FP16) instead of corrupting / failing the copy.
|
// the host recreates the ring as FP16) instead of corrupting / failing the copy.
|
||||||
let mut desc = D3D11_TEXTURE2D_DESC::default();
|
let mut desc = D3D11_TEXTURE2D_DESC::default();
|
||||||
|
// SAFETY: `surface` is a live ID3D11Texture2D (borrowed from IddCx); `desc` is a valid local out-param.
|
||||||
unsafe { surface.GetDesc(&mut desc) };
|
unsafe { surface.GetDesc(&mut desc) };
|
||||||
if desc.Format.0 as u32 != self.ring_format {
|
if desc.Format.0 as u32 != self.ring_format {
|
||||||
return;
|
return;
|
||||||
@@ -262,6 +288,8 @@ impl FramePublisher {
|
|||||||
for attempt in 0..ring_len {
|
for attempt in 0..ring_len {
|
||||||
let slot = (start + attempt) % ring_len;
|
let slot = (start + attempt) % ring_len;
|
||||||
let s = &self.slots[slot as usize];
|
let s = &self.slots[slot as usize];
|
||||||
|
// SAFETY: `s.mutex` is the live keyed mutex on this ring slot's shared texture; a 0 ms
|
||||||
|
// try-acquire of key 0 (released below or on WAIT_TIMEOUT it's never held).
|
||||||
match unsafe { s.mutex.AcquireSync(0, 0) } {
|
match unsafe { s.mutex.AcquireSync(0, 0) } {
|
||||||
Ok(()) => {
|
Ok(()) => {
|
||||||
// STRAIGHT-LINE, NO `?` between acquire + release — a `?`-return here would leak the
|
// STRAIGHT-LINE, NO `?` between acquire + release — a `?`-return here would leak the
|
||||||
@@ -269,6 +297,8 @@ impl FramePublisher {
|
|||||||
// the CopyResource is GPU-ordered before the consumer via the slot keyed mutex, and the
|
// the CopyResource is GPU-ordered before the consumer via the slot keyed mutex, and the
|
||||||
// `latest` store (Release) publishes the slot only AFTER the copy is queued + the mutex
|
// `latest` store (Release) publishes the slot only AFTER the copy is queued + the mutex
|
||||||
// released.
|
// released.
|
||||||
|
// SAFETY: `s.tex`/`surface` are live, format-matched (checked above) D3D textures on
|
||||||
|
// `self.context`'s device; the keyed mutex is held here, so we release it exactly once.
|
||||||
unsafe {
|
unsafe {
|
||||||
self.context.CopyResource(&s.tex, surface);
|
self.context.CopyResource(&s.tex, surface);
|
||||||
let _ = s.mutex.ReleaseSync(0);
|
let _ = s.mutex.ReleaseSync(0);
|
||||||
@@ -285,6 +315,8 @@ impl FramePublisher {
|
|||||||
}
|
}
|
||||||
.pack();
|
.pack();
|
||||||
self.latest_cell().store(latest, Ordering::Release);
|
self.latest_cell().store(latest, Ordering::Release);
|
||||||
|
// SAFETY: `self.event` is the live host-created frame-ready event we opened with
|
||||||
|
// EVENT_MODIFY_STATE; signalling it wakes the host consumer.
|
||||||
unsafe {
|
unsafe {
|
||||||
let _ = SetEvent(self.event);
|
let _ = SetEvent(self.event);
|
||||||
}
|
}
|
||||||
@@ -304,6 +336,8 @@ impl Drop for FramePublisher {
|
|||||||
// Slots FIRST (release the shared textures + keyed mutexes), THEN unmap the header, THEN the
|
// Slots FIRST (release the shared textures + keyed mutexes), THEN unmap the header, THEN the
|
||||||
// handles.
|
// handles.
|
||||||
self.slots.clear();
|
self.slots.clear();
|
||||||
|
// SAFETY: drop runs once; `self.header` (if non-null) is the live mapped view and `self.event`/
|
||||||
|
// `self.map` are the live handles this publisher opened — each unmapped/closed exactly once here.
|
||||||
unsafe {
|
unsafe {
|
||||||
if !self.header.is_null() {
|
if !self.header.is_null() {
|
||||||
let _ = UnmapViewOfFile(MEMORY_MAPPED_VIEW_ADDRESS {
|
let _ = UnmapViewOfFile(MEMORY_MAPPED_VIEW_ADDRESS {
|
||||||
|
|||||||
@@ -87,6 +87,8 @@ pub fn display_info(
|
|||||||
refresh_rate: u32,
|
refresh_rate: u32,
|
||||||
) -> wdk_sys::DISPLAYCONFIG_VIDEO_SIGNAL_INFO {
|
) -> wdk_sys::DISPLAYCONFIG_VIDEO_SIGNAL_INFO {
|
||||||
let clock_rate = refresh_rate * (height + 4) * (height + 4) + 1000;
|
let clock_rate = refresh_rate * (height + 4) * (height + 4) + 1000;
|
||||||
|
// SAFETY: building a C POD — the all-zero bit pattern is a valid uninitialized
|
||||||
|
// DISPLAYCONFIG_VIDEO_SIGNAL_INFO; every meaningful field is assigned below.
|
||||||
let mut si: wdk_sys::DISPLAYCONFIG_VIDEO_SIGNAL_INFO = unsafe { core::mem::zeroed() };
|
let mut si: wdk_sys::DISPLAYCONFIG_VIDEO_SIGNAL_INFO = unsafe { core::mem::zeroed() };
|
||||||
si.pixelRate = u64::from(clock_rate);
|
si.pixelRate = u64::from(clock_rate);
|
||||||
si.hSyncFreq = wdk_sys::DISPLAYCONFIG_RATIONAL {
|
si.hSyncFreq = wdk_sys::DISPLAYCONFIG_RATIONAL {
|
||||||
@@ -118,6 +120,8 @@ pub fn target_mode(width: u32, height: u32, refresh_rate: u32) -> iddcx::IDDCX_T
|
|||||||
cx: width,
|
cx: width,
|
||||||
cy: height,
|
cy: height,
|
||||||
};
|
};
|
||||||
|
// SAFETY: building a C POD — the all-zero bit pattern is a valid uninitialized
|
||||||
|
// DISPLAYCONFIG_VIDEO_SIGNAL_INFO; every meaningful field is assigned below.
|
||||||
let mut si: wdk_sys::DISPLAYCONFIG_VIDEO_SIGNAL_INFO = unsafe { core::mem::zeroed() };
|
let mut si: wdk_sys::DISPLAYCONFIG_VIDEO_SIGNAL_INFO = unsafe { core::mem::zeroed() };
|
||||||
si.pixelRate = u64::from(refresh_rate) * u64::from(width) * u64::from(height);
|
si.pixelRate = u64::from(refresh_rate) * u64::from(width) * u64::from(height);
|
||||||
si.hSyncFreq = wdk_sys::DISPLAYCONFIG_RATIONAL {
|
si.hSyncFreq = wdk_sys::DISPLAYCONFIG_RATIONAL {
|
||||||
@@ -134,6 +138,8 @@ pub fn target_mode(width: u32, height: u32, refresh_rate: u32) -> iddcx::IDDCX_T
|
|||||||
wdk_sys::DISPLAYCONFIG_SCANLINE_ORDERING::DISPLAYCONFIG_SCANLINE_ORDERING_PROGRESSIVE;
|
wdk_sys::DISPLAYCONFIG_SCANLINE_ORDERING::DISPLAYCONFIG_SCANLINE_ORDERING_PROGRESSIVE;
|
||||||
// videoStandard=255, vSyncFreqDivider=1 (bits 16..21) => 255 | (1<<16).
|
// videoStandard=255, vSyncFreqDivider=1 (bits 16..21) => 255 | (1<<16).
|
||||||
si.__bindgen_anon_1.videoStandard = 255 | (1 << 16);
|
si.__bindgen_anon_1.videoStandard = 255 | (1 << 16);
|
||||||
|
// SAFETY: building a C POD — the all-zero bit pattern is a valid uninitialized IDDCX_TARGET_MODE;
|
||||||
|
// the required `.Size` (+ signal info) are set immediately below.
|
||||||
let mut tm: iddcx::IDDCX_TARGET_MODE = unsafe { core::mem::zeroed() };
|
let mut tm: iddcx::IDDCX_TARGET_MODE = unsafe { core::mem::zeroed() };
|
||||||
tm.Size = core::mem::size_of::<iddcx::IDDCX_TARGET_MODE>() as u32;
|
tm.Size = core::mem::size_of::<iddcx::IDDCX_TARGET_MODE>() as u32;
|
||||||
tm.TargetVideoSignalInfo = wdk_sys::DISPLAYCONFIG_TARGET_MODE {
|
tm.TargetVideoSignalInfo = wdk_sys::DISPLAYCONFIG_TARGET_MODE {
|
||||||
@@ -151,6 +157,8 @@ pub fn target_mode(width: u32, height: u32, refresh_rate: u32) -> iddcx::IDDCX_T
|
|||||||
pub fn wire_bits() -> iddcx::IDDCX_WIRE_BITS_PER_COMPONENT {
|
pub fn wire_bits() -> iddcx::IDDCX_WIRE_BITS_PER_COMPONENT {
|
||||||
let rgb = iddcx::IDDCX_BITS_PER_COMPONENT::IDDCX_BITS_PER_COMPONENT_8
|
let rgb = iddcx::IDDCX_BITS_PER_COMPONENT::IDDCX_BITS_PER_COMPONENT_8
|
||||||
| iddcx::IDDCX_BITS_PER_COMPONENT::IDDCX_BITS_PER_COMPONENT_10;
|
| iddcx::IDDCX_BITS_PER_COMPONENT::IDDCX_BITS_PER_COMPONENT_10;
|
||||||
|
// SAFETY: building a C POD — the all-zero bit pattern is a valid uninitialized
|
||||||
|
// IDDCX_WIRE_BITS_PER_COMPONENT; every field is assigned below.
|
||||||
let mut w: iddcx::IDDCX_WIRE_BITS_PER_COMPONENT = unsafe { core::mem::zeroed() };
|
let mut w: iddcx::IDDCX_WIRE_BITS_PER_COMPONENT = unsafe { core::mem::zeroed() };
|
||||||
w.Rgb = rgb;
|
w.Rgb = rgb;
|
||||||
w.YCbCr444 = iddcx::IDDCX_BITS_PER_COMPONENT::IDDCX_BITS_PER_COMPONENT_NONE;
|
w.YCbCr444 = iddcx::IDDCX_BITS_PER_COMPONENT::IDDCX_BITS_PER_COMPONENT_NONE;
|
||||||
@@ -164,6 +172,8 @@ pub fn wire_bits() -> iddcx::IDDCX_WIRE_BITS_PER_COMPONENT {
|
|||||||
/// zeroed.
|
/// zeroed.
|
||||||
pub fn target_mode2(width: u32, height: u32, refresh_rate: u32) -> iddcx::IDDCX_TARGET_MODE2 {
|
pub fn target_mode2(width: u32, height: u32, refresh_rate: u32) -> iddcx::IDDCX_TARGET_MODE2 {
|
||||||
let m1 = target_mode(width, height, refresh_rate);
|
let m1 = target_mode(width, height, refresh_rate);
|
||||||
|
// SAFETY: building a C POD — the all-zero bit pattern is a valid uninitialized IDDCX_TARGET_MODE2;
|
||||||
|
// the required `.Size` (+ signal info + bit depth) are set immediately below.
|
||||||
let mut tm: iddcx::IDDCX_TARGET_MODE2 = unsafe { core::mem::zeroed() };
|
let mut tm: iddcx::IDDCX_TARGET_MODE2 = unsafe { core::mem::zeroed() };
|
||||||
tm.Size = core::mem::size_of::<iddcx::IDDCX_TARGET_MODE2>() as u32;
|
tm.Size = core::mem::size_of::<iddcx::IDDCX_TARGET_MODE2>() as u32;
|
||||||
tm.TargetVideoSignalInfo = m1.TargetVideoSignalInfo;
|
tm.TargetVideoSignalInfo = m1.TargetVideoSignalInfo;
|
||||||
@@ -275,12 +285,18 @@ pub fn create_monitor(
|
|||||||
|
|
||||||
// EDID (serial = id) describes the monitor; the OS calls back into parse_monitor_description.
|
// EDID (serial = id) describes the monitor; the OS calls back into parse_monitor_description.
|
||||||
let mut edid = crate::edid::Edid::generate_with(id);
|
let mut edid = crate::edid::Edid::generate_with(id);
|
||||||
|
// SAFETY: building a C POD — the all-zero bit pattern is a valid uninitialized
|
||||||
|
// IDDCX_MONITOR_DESCRIPTION; the required `.Size`/Type/DataSize/pData are set immediately below.
|
||||||
let mut desc: iddcx::IDDCX_MONITOR_DESCRIPTION = unsafe { core::mem::zeroed() };
|
let mut desc: iddcx::IDDCX_MONITOR_DESCRIPTION = unsafe { core::mem::zeroed() };
|
||||||
desc.Size = core::mem::size_of::<iddcx::IDDCX_MONITOR_DESCRIPTION>() as u32;
|
desc.Size = core::mem::size_of::<iddcx::IDDCX_MONITOR_DESCRIPTION>() as u32;
|
||||||
desc.Type = iddcx::IDDCX_MONITOR_DESCRIPTION_TYPE::IDDCX_MONITOR_DESCRIPTION_TYPE_EDID;
|
desc.Type = iddcx::IDDCX_MONITOR_DESCRIPTION_TYPE::IDDCX_MONITOR_DESCRIPTION_TYPE_EDID;
|
||||||
desc.DataSize = edid.len() as u32;
|
desc.DataSize = edid.len() as u32;
|
||||||
|
// SAFETY: `edid` is a local Vec that outlives this `create_monitor` call; IddCxMonitorCreate (below)
|
||||||
|
// reads through `pData` SYNCHRONOUSLY, before `edid` drops — the pointer never escapes the call.
|
||||||
desc.pData = edid.as_mut_ptr().cast();
|
desc.pData = edid.as_mut_ptr().cast();
|
||||||
|
|
||||||
|
// SAFETY: building a C POD — the all-zero bit pattern is a valid uninitialized IDDCX_MONITOR_INFO;
|
||||||
|
// the required `.Size` (+ container id / type / connector / description) are set immediately below.
|
||||||
let mut info: iddcx::IDDCX_MONITOR_INFO = unsafe { core::mem::zeroed() };
|
let mut info: iddcx::IDDCX_MONITOR_INFO = unsafe { core::mem::zeroed() };
|
||||||
info.Size = core::mem::size_of::<iddcx::IDDCX_MONITOR_INFO>() as u32;
|
info.Size = core::mem::size_of::<iddcx::IDDCX_MONITOR_INFO>() as u32;
|
||||||
info.MonitorContainerId = container_guid(id);
|
info.MonitorContainerId = container_guid(id);
|
||||||
@@ -289,6 +305,8 @@ pub fn create_monitor(
|
|||||||
info.ConnectorIndex = id;
|
info.ConnectorIndex = id;
|
||||||
info.MonitorDescription = desc;
|
info.MonitorDescription = desc;
|
||||||
|
|
||||||
|
// SAFETY: building a C POD — the all-zero bit pattern is a valid uninitialized WDF_OBJECT_ATTRIBUTES;
|
||||||
|
// the required `.Size` (+ execution/sync scope) are set immediately below.
|
||||||
let mut attr: wdk_sys::WDF_OBJECT_ATTRIBUTES = unsafe { core::mem::zeroed() };
|
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.Size = core::mem::size_of::<wdk_sys::WDF_OBJECT_ATTRIBUTES>() as u32;
|
||||||
attr.ExecutionLevel = wdk_sys::_WDF_EXECUTION_LEVEL::WdfExecutionLevelInheritFromParent;
|
attr.ExecutionLevel = wdk_sys::_WDF_EXECUTION_LEVEL::WdfExecutionLevelInheritFromParent;
|
||||||
@@ -299,6 +317,8 @@ pub fn create_monitor(
|
|||||||
ObjectAttributes: &raw mut attr,
|
ObjectAttributes: &raw mut attr,
|
||||||
pMonitorInfo: &raw mut info,
|
pMonitorInfo: &raw mut info,
|
||||||
};
|
};
|
||||||
|
// SAFETY: building a C POD — the all-zero bit pattern is a valid uninitialized IDARG_OUT_MONITORCREATE
|
||||||
|
// (an out-param the framework fills).
|
||||||
let mut create_out: iddcx::IDARG_OUT_MONITORCREATE = unsafe { core::mem::zeroed() };
|
let mut create_out: iddcx::IDARG_OUT_MONITORCREATE = unsafe { core::mem::zeroed() };
|
||||||
// SAFETY: adapter is a valid IddCx adapter; create_in points to valid local storage read synchronously.
|
// SAFETY: adapter is a valid IddCx adapter; create_in points to valid local storage read synchronously.
|
||||||
let st = unsafe { wdk_iddcx::IddCxMonitorCreate(adapter, &create_in, &mut create_out) };
|
let st = unsafe { wdk_iddcx::IddCxMonitorCreate(adapter, &create_in, &mut create_out) };
|
||||||
@@ -315,6 +335,8 @@ pub fn create_monitor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Tell the OS the monitor is plugged in.
|
// Tell the OS the monitor is plugged in.
|
||||||
|
// SAFETY: building a C POD — the all-zero bit pattern is a valid uninitialized IDARG_OUT_MONITORARRIVAL
|
||||||
|
// (an out-param the framework fills).
|
||||||
let mut arrival_out: iddcx::IDARG_OUT_MONITORARRIVAL = unsafe { core::mem::zeroed() };
|
let mut arrival_out: iddcx::IDARG_OUT_MONITORARRIVAL = unsafe { core::mem::zeroed() };
|
||||||
// SAFETY: `monitor` is the just-created IddCx monitor handle.
|
// SAFETY: `monitor` is the just-created IddCx monitor handle.
|
||||||
let st = unsafe { wdk_iddcx::IddCxMonitorArrival(monitor, &mut arrival_out) };
|
let st = unsafe { wdk_iddcx::IddCxMonitorArrival(monitor, &mut arrival_out) };
|
||||||
|
|||||||
@@ -112,6 +112,8 @@ impl SwapChainProcessor {
|
|||||||
// Service. It will intelligently prioritize the thread for improved throughput in high
|
// Service. It will intelligently prioritize the thread for improved throughput in high
|
||||||
// CPU-load scenarios.
|
// CPU-load scenarios.
|
||||||
let mut av_task = 0u32;
|
let mut av_task = 0u32;
|
||||||
|
// SAFETY: `w!("Distribution")` is a 'static null-terminated UTF-16 task name; `av_task` is a
|
||||||
|
// valid local out-param. The returned handle is reverted with AvRevertMmThreadCharacteristics.
|
||||||
let res = unsafe { AvSetMmThreadCharacteristicsW(w!("Distribution"), &mut av_task) };
|
let res = unsafe { AvSetMmThreadCharacteristicsW(w!("Distribution"), &mut av_task) };
|
||||||
let Ok(av_handle) = res else {
|
let Ok(av_handle) = res else {
|
||||||
dbglog!("[pf-vd] swap-chain: failed to prioritize thread: {res:?}");
|
dbglog!("[pf-vd] swap-chain: failed to prioritize thread: {res:?}");
|
||||||
@@ -141,6 +143,8 @@ impl SwapChainProcessor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Revert the thread to normal once it's done.
|
// Revert the thread to normal once it's done.
|
||||||
|
// SAFETY: `av_handle` is the live characteristics handle returned by AvSetMmThreadCharacteristicsW
|
||||||
|
// above, reverted exactly once here at thread exit.
|
||||||
let res = unsafe { AvRevertMmThreadCharacteristics(av_handle) };
|
let res = unsafe { AvRevertMmThreadCharacteristics(av_handle) };
|
||||||
if let Err(e) = res {
|
if let Err(e) = res {
|
||||||
dbglog!("[pf-vd] swap-chain: failed to revert prioritized thread: {e:?}");
|
dbglog!("[pf-vd] swap-chain: failed to revert prioritized thread: {e:?}");
|
||||||
@@ -179,6 +183,8 @@ impl SwapChainProcessor {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
// Built zeroed + field-assigned (driver style) — robust against a bindgen field-set difference.
|
// Built zeroed + field-assigned (driver style) — robust against a bindgen field-set difference.
|
||||||
|
// SAFETY: building a C POD — the all-zero bit pattern is a valid uninitialized
|
||||||
|
// IDARG_IN_SWAPCHAINSETDEVICE; the `pDevice` field is set immediately below.
|
||||||
let mut set_device: IDARG_IN_SWAPCHAINSETDEVICE = unsafe { core::mem::zeroed() };
|
let mut set_device: IDARG_IN_SWAPCHAINSETDEVICE = unsafe { core::mem::zeroed() };
|
||||||
set_device.pDevice = dxgi_device.as_raw().cast();
|
set_device.pDevice = dxgi_device.as_raw().cast();
|
||||||
let mut set_ok = false;
|
let mut set_ok = false;
|
||||||
@@ -274,6 +280,8 @@ impl SwapChainProcessor {
|
|||||||
// the GPU surface (out.MetaData.pSurface) — STEP 6 publishes it into the shared ring in the
|
// the GPU surface (out.MetaData.pSurface) — STEP 6 publishes it into the shared ring in the
|
||||||
// success branch below. Built zeroed + field-assigned (driver style) so a bindgen field-set
|
// success branch below. Built zeroed + field-assigned (driver style) so a bindgen field-set
|
||||||
// difference can't break a positional struct literal.
|
// difference can't break a positional struct literal.
|
||||||
|
// SAFETY: building a C POD — the all-zero bit pattern is a valid uninitialized
|
||||||
|
// IDARG_IN_RELEASEANDACQUIREBUFFER2; the required `.Size`/AcquireSystemMemoryBuffer are set below.
|
||||||
let mut in_args: IDARG_IN_RELEASEANDACQUIREBUFFER2 = unsafe { core::mem::zeroed() };
|
let mut in_args: IDARG_IN_RELEASEANDACQUIREBUFFER2 = unsafe { core::mem::zeroed() };
|
||||||
#[allow(clippy::cast_possible_truncation)]
|
#[allow(clippy::cast_possible_truncation)]
|
||||||
{
|
{
|
||||||
@@ -283,6 +291,8 @@ impl SwapChainProcessor {
|
|||||||
// `core::mem::zeroed()` (not `::default()`) — consistent with every other IddCx out-struct
|
// `core::mem::zeroed()` (not `::default()`) — consistent with every other IddCx out-struct
|
||||||
// in this driver, and robust whether or not bindgen derives `Default` for this type (its
|
// in this driver, and robust whether or not bindgen derives `Default` for this type (its
|
||||||
// `MetaData` field carries a raw `pSurface` pointer + union which can suppress the derive).
|
// `MetaData` field carries a raw `pSurface` pointer + union which can suppress the derive).
|
||||||
|
// SAFETY: building a C POD — the all-zero bit pattern is a valid uninitialized
|
||||||
|
// IDARG_OUT_RELEASEANDACQUIREBUFFER2 (an out-param the framework fills).
|
||||||
let mut buffer: IDARG_OUT_RELEASEANDACQUIREBUFFER2 = unsafe { core::mem::zeroed() };
|
let mut buffer: IDARG_OUT_RELEASEANDACQUIREBUFFER2 = unsafe { core::mem::zeroed() };
|
||||||
// SAFETY: driver is loaded; `swap_chain` is valid; in/out point to valid local storage.
|
// SAFETY: driver is loaded; `swap_chain` is valid; in/out point to valid local storage.
|
||||||
let hr: NTSTATUS = unsafe {
|
let hr: NTSTATUS = unsafe {
|
||||||
|
|||||||
Reference in New Issue
Block a user