Stop battery EcoQoS from starving frame capture during recording
Template tests / tests (push) Successful in 1m41s

Confirmed cause: on battery in a power-saving plan, Windows applies Power
Throttling (EcoQoS) to background work. StepForge records with its window
hidden, so the frame-capture worker renderer (plus the GPU and screen-capture
utility processes feeding it) get CPU-throttled. The throttled worker can't
sample the screen fast enough, so every click finds no fresh pre-click frame
and falls back to a slow post-click fresh shot — producing out-of-order,
dropped steps. It only reproduced on battery; plugged in (where EcoQoS stays
off even in eco mode) it worked. The capture log showed every click detected
but every one reporting "no frame qualified", with a ~4.6s stream startup.

Fixes:
- Chromium switches (disable-background-timer-throttling,
  disable-renderer-backgrounding, disable-backgrounding-occluded-windows) so
  Chromium stops de-prioritising and timer-throttling the hidden worker.
- New app/win-power.js opts the OS processes out of EcoQoS via
  SetProcessInformation(ProcessPowerThrottling, EXECUTION_SPEED off) and
  raises them to high priority. Applied to all live Electron processes when a
  session starts/resumes, and to the worker renderer the moment it is created
  (before it begins streaming). Best-effort, no-op off Windows.

The earlier mouse-hook EcoQoS opt-out already fixed click *detection*; this
fixes the frame-capture side that the same throttling was breaking.
This commit is contained in:
2026-06-15 18:45:50 -05:00
parent 94f14851fa
commit f580759337
3 changed files with 125 additions and 0 deletions
+8
View File
@@ -340,6 +340,14 @@ async function createElectronHost(onEvent) {
if (!win.isDestroyed()) win.destroy();
throw err;
}
// The worker is a hidden renderer, so on battery Windows would EcoQoS-throttle
// it and starve frame sampling. Clear that on its own OS process before it
// starts streaming. Best-effort; no-op off Windows.
try {
// eslint-disable-next-line global-require
const { keepProcessesResponsive } = require('./win-power');
if (!win.isDestroyed()) keepProcessesResponsive([win.webContents.getOSProcessId()]);
} catch { /* throttling tweak is optional */ }
return {
send(msg) {
if (!win.isDestroyed()) win.webContents.send('capture-worker:command', msg);