Skip to content

perf: light background mode, idle input cadence and performance measurement - #240

Merged
nextestudios merged 5 commits into
mainfrom
feature/perf-light-gaming
Sep 28, 2026
Merged

nextestudios merged 5 commits into
mainfrom
feature/perf-light-gaming

Conversation

@nextestudios

@nextestudios nextestudios commented Sep 28, 2026 •

Copy link
Copy Markdown
Owner

Summary

Makes ControlFS light enough to sit next to a game, measured before and after.

  • Measurement (build/Measure-Performance.ps1, new step in the manual Smoke workflow, full mode only; no new workflows or triggers): launches the portable exe (first run with an empty extraction dir + 3 warm runs) and the installed exe, and records ms to "Janela ativada" / "Primeiro quadro" (new precise log lines), CPU %, working set, private bytes and threads over 10 s idle in the foreground and 10 s minimized (ShowWindow(SW_MINIMIZE)), plus package sizes. Table goes to the job summary and the performance artifact; a run whose app exits mid-measurement fails the step.
  • Bug found by the measurement: minimizing the window crashed the app on main (fail-fast E_INVALIDARG from a WinUI AppWindow.Changed handler reading the minimized title bar; dump analysis in the smoke log). Fixed: title-bar updates skip the minimized state and never let an exception escape that event; minimizing no longer relayouts.
  • Idle cadence (InputCadence): the 8 ms ticker + 1 ms Windows timer now run only with the window in front and a controller connected; with no controller the UI timer polls at 250 ms just to notice one arriving (keyboard/mouse come through XAML events).
  • Light in the background (BackgroundModePolicy, new setting Leve em segundo plano, on by default): when the window is inactive or minimized (a minimized sole window stays "active", so the presenter state counts too): BelowNormal priority + EcoQoS, controllers only watched for connections every 1 s with SDL input events disabled (a game using the same pad no longer floods our queue; stick/trigger state is resynced silently on return), drive polling paused (checked immediately on return), and after 5 s a single aggressive GC + LOH compaction + working-set trim. Activation restores at once; media playing keeps normal priority.
  • Startup/memory: automatic update check deferred until after the first frame; icon caches capped by size (details panel 32, cards/nav 128, tiles 256, rows 512); logo decoded at display size. Already lazy and verified: LibGit2 native (only with Git status on), Media Foundation/PDF (first use). SDL init measured 16–32 ms, kept on the critical path.
  • Packaging decisions (measured on experiment branches, not merged): ReadyToRun off (+21 MB portable / +9 MB installer, no startup gain); single-file compression kept (171 MB vs 70.5 MB uncompressed; only the first launch pays ~0.25 s).

Before / after (GitHub windows-latest, WARP, no controller; median of 3 Smoke runs, each the median of 3 warm launches; installed in parentheses)

Metric Before (main + crash fix) After
To window (ms) 1031 (933) 901 (851)
To first frame (ms) 1274 (1169) 1099 (1058)
Idle CPU, foreground (% of one core) 1.72 (1.41) 0.47 (0.62)
Working set, foreground (MB) 155.3 (155.1) 152.8 (152.4)
Private bytes, foreground (MB) 61.7 (62.2) 60.5 (60.6)
Threads, foreground 35 (36) 34 (35)
CPU minimized (% of one core) 2.03 (1.41) 0.47 (0.47)
Working set minimized (MB) 153.1 (152.9) 17.9 (17.8)
Private bytes minimized (MB) 61.7 (62.3) 55.7 (55.7)
Threads minimized 34 (35) 32 (33)
Portable / installer / installed folder (MB) 70.5 / 46.6 / 182.4 70.5 / 46.6 / 182.4

Pure main could not be measured minimized (it crashed). Runner-to-runner noise is large (the same build measured 543–1100 ms to window), so the startup difference is within noise. Details and methodology in docs/performance.md.

Tests

  • CI: InputCadenceTests (2), BackgroundModePolicyTests (2), UpdateFlowTests::Automatic_check_waits_for_the_first_frame_when_deferred.
  • Full Smoke (launch, installer, UIA, dump analysis, measurement) on this branch: green in runs 36376541012, 36378316173, 36378320665. Run 36377487211 failed only in the dev-only glyph gallery of --render-screens (UIElementCollection.Add E_ABORT at the very end), a flake also seen on the measurement-only commit (36375709199) and not touching code changed here.
  • Manual (no controller/GPU on the runner): gaming with the same controller while ControlFS is behind, EcoQoS leaf in Task Manager, no ghost action on return, controller arrival within 250 ms, long extraction and music while minimized — added to docs/TESTING.md ("Leve em segundo plano e repouso").

Docs: CHANGELOG pair (Desempenho/Performance + fix), GUIDE pair (new setting), docs/ui-redesign.md row, docs/performance.md rewritten.

🤖 Generated with Claude Code

nextestudios and others added 5 commits September 28, 2026 00:57
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@nextestudios
nextestudios marked this pull request as ready for review September 28, 2026 04:49
@nextestudios
nextestudios merged commit f0d094e into main Sep 28, 2026
1 check passed
@nextestudios
nextestudios deleted the feature/perf-light-gaming branch September 28, 2026 04:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant