Expand description
Windows cross-vendor WDDM backend (docs/design/cross-platform.md §3) — AMD and Intel
on Windows (and NVIDIA when NVML is absent), entirely from OS surfaces: DXGI for
enumeration and VRAM totals, PDH GPU counters for utilization and memory, D3DKMT for
the LUID→PCI identity. pdh.dll/gdi32.dll/dxgi.dll are OS system libraries —
linking them via the windows crate does NOT violate the no-vendor-SDK rule (that rule
targets vendor SDKs with soname churn, not the OS itself).
Honesty contract, per metric (§3.4 — these are load-bearing semantics, not trivia):
- util_pct is scheduler duty-cycle, not capacity. The PDH
GPU Enginecounters report how busy the WDDM scheduler (VidSch) kept each engine — time with work resident, exactly the same kind of number as NVML’s duty-cycle “utilization”, and it must be labeled exactly as honestly: a GPU can read 100% here while its execution units idle. The device headline is the busiest single engine (summed across pids per engine first), which is what Task Manager’s Performance tab shows — comparable on purpose, so users cross-checking against Task Manager see agreement, not a mystery. - mem_used_bytes comes from
GPU Adapter Memory\Dedicated Usage— the VidMm (Windows video memory manager) number, the adapter-level counter Microsoft confirms stays correct (KB4490156). DXGI’sQueryVideoMemoryInfois never used for device-used: it reports the calling process’s budget/usage by design and would show gpuviewer’s own ~0 — a silently-wrong number, the worst kind. - temp_c, power_mw, fan_pct, sm_clock_mhz, mem_clock_mhz are
Noneon this backend — Windows exposes no public temperature/power/clock API for AMD/Intel GPUs without installing vendor SDKs (ADLX/IGCL), which the no-vendor-SDK rule forbids. The UI renders these as unavailable; fabricating them is not an option. The two known semi-public paths are explicitly out of scope per §3.6: D3DKMTKMTQAITYPE_ADAPTERPERFDATA(driver-optional kernel thunk, deci-°C, power in 0.1% units) is a future opportunistic probe, and DXCore’s QueryState telemetry is still prerelease. Do not “fix” these in. - throttle is
None: this source cannot observe throttling at all, and the §5.4Option<ThrottleReasons>model makes that unobservability representable —Nonemeans “unobserved”, and the event engine/UI/rollups treat it as a blind spot. The all-false struct (a fabricated fact-grade “not throttling”) is never emitted here. - Per-process rows come from PDH
GPU Engine/GPU Process Memoryinstances joined by (pid, LUID).kindisUnknown— PDH does not distinguish compute/graphics; an engtype ofCompute/Cudaupgrades toComputeas a labeled heuristic only.cpu_pct/containerareNone(no/procon Windows). - Absence is normal. A GPU-less machine (CI runner, RDP session without vGPU) has
no
GPU EnginePDH object at all (PDH_CSTATUS_NO_OBJECT) and possibly no hardware DXGI adapter — every such outcome isNone/empty/skipped-backend, never an error. The first PDH collection legitimately yields no rate data (rate counters need two collections) — the first frame is honestly empty.
Identity (§3.1): the in-session join key is the adapter LUID (matches PDH instance
tokens and D3DKMT). A LUID is session-scoped — it changes on reboot/driver update — so
it is never persisted as identity. The persistent DeviceId is the normalized PCI
BDF ("0000:bb:dd.f") obtained via D3DKMT ADAPTERADDRESS, the same key shape NVML
and sysfs produce, so history identity works and the collector’s first-wins PCI dedupe
lets NVML claim NVIDIA boards ahead of this backend (registry order nvidia → wddm,
§3.7). If the D3DKMT thunk fails, the fallback id wddm:<vendor>:<device>:<ordinal>
deliberately does NOT parse as a PCI address, so normalize_pci_id refuses to dedupe
it: listing a device twice beats wrongly merging two.
Layout: the pdh and adapters submodules carry the §9 interface-freeze surface
(pdh::shared(), SharedPdh::snapshot, parse_instance, adapters::enumerate); the
integrator may re-export them as win::pdh/win::adapters or split them into files
later. Everything that touches a Windows API is #[cfg(target_os = "windows")]; the
counter-instance grammar and all aggregation math are pure functions that compile and
unit-test on every OS (CI has no GPUs — the Linux leg runs those tests from string
fixtures).