Problem
The F2 status overlay labels its GPU number as utilization, but on Windows the value shown is frameTimeMs / vsyncBudget × 100 — i.e. "how much of the vsync budget we spent rendering," not "how much of the GPU hardware is in use."
These are different measurements and can diverge significantly:
- With vsync on, the GPU may finish rendering and sit idle for the rest of the interval. Frame-budget ratio reports (say) 80% while the hardware is actually ~40% utilized.
- Under heavy fragment-shader work (e.g. cubic frame blending at 4K), frame-budget ratio can hit 100% while the GPU still has compute engines idle.
- Decode work on the Video Decode engine is invisible to the frame-budget ratio entirely.
The HUD label says "total" which users read as overall device utilization (matching Task Manager's GPU column), but that's not what they're getting on Windows.
Current state
Two separate measurements exist and are combined on the HUD at client.h:1974-1978:
float gpuMs = g_Player().Renderer()->GetGPUFrameTimeMs();
float gpuPct = g_Player().Renderer()->GetGPUUtilization();
std::string sysGpu = (m_GpuUsage >= 0) ? string_format(", %i%%", m_GpuUsage) : "";
// displayed as: "%.2fms (%.0f%%)%s total"
// ^^^^ ^^
// render% sysGpu
GetGPUUtilization() (renderer-provided) — frame-budget ratio. Implemented on both platforms.
GetGpuUsage() (platform-provided) — real device utilization. On Mac this reads IOKit's IOAccelerator → PerformanceStatistics → "Device Utilization %" (cpu_usage_mac.h:181-240) and appears on the HUD as ", NN% total".
- On Windows,
GetGpuUsage() in MSVC/cpu_usage_win32.h:128-131 just returns -1, so the "real" suffix is never shown. All the user sees is the misleading frame-budget ratio.
Proposed fix
Implement GetGpuUsage() on Windows using PDH performance counters — the same API Task Manager uses.
Scope: process-scoped (our PID only), max across engines. This answers "how much GPU is this app using" and matches the mental model users have when they see a GPU % next to a process.
Implementation sketch (in MSVC/cpu_usage_win32.h):
- Open a PDH query on first call (
PdhOpenQuery).
- Enumerate instances of
GPU Engine via PdhEnumObjectItems; filter to instances whose name contains pid_<our_pid>_ (PDH's GPU-engine instance naming convention: pid_<pid>_luid_<luid>_phys_<n>_eng_<m>_engtype_<type>).
PdhAddEnglishCounter for \GPU Engine(<instance>)\Utilization Percentage for each matching instance.
- Cache on ~1s sampling (match Mac's
GetGpuUsage cadence) to avoid PDH overhead per frame.
- On each sample:
PdhCollectQueryData, read each counter's formatted value, take the max across engines, clamp to [0,100].
- Return
-1 if PDH init fails, so the HUD suppression at client.h:1974 keeps working.
Handle the case where new child engines appear (rebuild the counter set periodically, or on a miss).
Follow-up consideration (separate issue?): the frame-budget ratio shown alongside is still useful — arguably rename it on the HUD so its meaning is clear (e.g. %.2fms (%.0f%% budget), %i%% GPU). Right now both numbers being labeled as GPU-related is what makes the "80% GPU for a video" reading surprising.
Repro
- Run a dream on Windows with F2 overlay enabled.
- Compare the HUD GPU % against Task Manager → Performance → GPU, or GPU-Z.
- Open Task Manager Details tab, add the "GPU Engine" column on the process — that's the ground truth we should match.
Problem
The F2 status overlay labels its GPU number as utilization, but on Windows the value shown is
frameTimeMs / vsyncBudget × 100— i.e. "how much of the vsync budget we spent rendering," not "how much of the GPU hardware is in use."These are different measurements and can diverge significantly:
The HUD label says "total" which users read as overall device utilization (matching Task Manager's GPU column), but that's not what they're getting on Windows.
Current state
Two separate measurements exist and are combined on the HUD at
client.h:1974-1978:GetGPUUtilization()(renderer-provided) — frame-budget ratio. Implemented on both platforms.GetGpuUsage()(platform-provided) — real device utilization. On Mac this reads IOKit'sIOAccelerator→PerformanceStatistics→"Device Utilization %"(cpu_usage_mac.h:181-240) and appears on the HUD as ", NN% total".GetGpuUsage()inMSVC/cpu_usage_win32.h:128-131just returns-1, so the "real" suffix is never shown. All the user sees is the misleading frame-budget ratio.Proposed fix
Implement
GetGpuUsage()on Windows using PDH performance counters — the same API Task Manager uses.Scope: process-scoped (our PID only), max across engines. This answers "how much GPU is this app using" and matches the mental model users have when they see a GPU % next to a process.
Implementation sketch (in
MSVC/cpu_usage_win32.h):PdhOpenQuery).GPU EngineviaPdhEnumObjectItems; filter to instances whose name containspid_<our_pid>_(PDH's GPU-engine instance naming convention:pid_<pid>_luid_<luid>_phys_<n>_eng_<m>_engtype_<type>).PdhAddEnglishCounterfor\GPU Engine(<instance>)\Utilization Percentagefor each matching instance.GetGpuUsagecadence) to avoid PDH overhead per frame.PdhCollectQueryData, read each counter's formatted value, take the max across engines, clamp to [0,100].-1if PDH init fails, so the HUD suppression atclient.h:1974keeps working.Handle the case where new child engines appear (rebuild the counter set periodically, or on a miss).
Follow-up consideration (separate issue?): the frame-budget ratio shown alongside is still useful — arguably rename it on the HUD so its meaning is clear (e.g.
%.2fms (%.0f%% budget), %i%% GPU). Right now both numbers being labeled as GPU-related is what makes the "80% GPU for a video" reading surprising.Repro