Fix Game Mode EcoQoS opt-out + cleanup batch for v2.0.5#14
Merged
Conversation
#1 (critical bug): ProcessOptimizationService.cs:80 was calling SetProcessInformation with processInformationClass=34 — the exact typo that App.OnStartup already had fixed to 4. With 34 the call returns ERROR_INVALID_PARAMETER and IGNORE_TIMER_RESOLUTION never applies, so games added to Game Mode kept getting EcoQoS-throttled to 1–15.6 ms in the background, defeating the whole point of the 0.5 ms global lock for those processes. Replaced both call sites (this one and App.xaml.cs) with a new NativeMethods.ProcessPowerThrottling = 4 named constant so the magic number can't be re-introduced. #2: SettingsViewModel.ImportSettings now raises a SettingsImported event with the freshly loaded snapshot; MainViewModel subscribes and re-applies it via ApplySettings, so the running session updates without a restart. The localized "restart to apply" success string is now obsolete and reads just "Settings imported". #3: Removed dead code — MeasureActualResolutionMs is no longer called anywhere (the watchdog reads `current` directly from NtSetTimerResolution), and its three P/Invokes (GetSystemTimeAsFileTime, TimeBeginPeriod, TimeEndPeriod) went with it. The legacy timeBeginPeriod path had an explicit warning comment about why it must never be re-enabled. #4: ApplyTimerRequest used to register the *caller* thread as AVRT "Pro Audio" and drop the returned handle on the floor — that permanently boosted the dispatcher to MMCSS and leaked one handle per Activate cycle. The dedicated watchdog thread already does MMCSS registration AND reverts it on exit (handle stored in a local, AvRevertMmThreadCharacteristics called in finally), so the caller-thread call was both wrong (per-process kernel request, not per-thread) and leaky. Removed. #5: SyncMonitorSettings split into SyncMonitorThresholds (called on every TextBox keystroke) and SyncMonitorGames (called only when the Games collection mutates). Stops rebuilding the GamePaths list on every digit the user types into a threshold input. Bumped to 2.0.5 in csproj, Setup.iss and build-installer.ps1. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
v2.0.5 ships one critical bug fix (Game Mode silently wasn't doing its main job on Win 11) plus four cleanup wins from an internal code audit.
#1 — Fix Game Mode EcoQoS opt-out (regression bug)
ProcessOptimizationService.ApplyTimerResolutionOptOutwas callingSetProcessInformation(handle, 34, …). PROCESS_INFORMATION_CLASS::ProcessPowerThrottling is 4, not 34 — the exact typo that was already fixed inApp.OnStartup. With 34 the kernel returnsERROR_INVALID_PARAMETER (87)andIGNORE_TIMER_RESOLUTIONnever applies, so any game added to Game Mode kept getting EcoQoS-throttled to 1–15.6 ms in the background. The 0.5 ms global lock simply didn't reach those processes.Fix: replaced both literals with
NativeMethods.ProcessPowerThrottling = 4(new named constant) so the magic number can't be re-introduced.#2 — Import Settings now applies live
SettingsViewModel.ImportSettingswas writing to the registry but not re-applying values to the running session — users had to restart for an import to take effect. Now it raises aSettingsImportedevent with the loaded snapshot;MainViewModelsubscribes and re-runsApplySettings. The "restart to apply" success string is updated accordingly.#3 — Dead code removed
TimerResolutionService.MeasureActualResolutionMs— unused since the watchdog readscurrentdirectly fromNtSetTimerResolution.NativeMethods.GetSystemTimeAsFileTime— only existed forMeasureActualResolutionMs.NativeMethods.TimeBeginPeriod/TimeEndPeriod— explicit comment inApplyTimerRequestsays these must never be called (Win 11 clamps Nt request to 1 ms).#4 — AVRT handle leak in ApplyTimerRequest
The activation path was calling
AvSetMmThreadCharacteristics("Pro Audio", …)on the caller thread (the dispatcher), then discarding the returned handle. That permanently boosted the UI thread to MMCSS and leaked one avrt handle per Activate/Deactivate cycle. The dedicated watchdog thread already does AVRT correctly (stores handle, reverts infinally), and the kernel request is per-process — not per-thread — so the caller-thread call had no benefit. Removed.#5 — Split SyncMonitorSettings
SyncMonitorSettings()was rebuilding the entireGamePathslist (Select(...).ToList()) on every keystroke into the threshold textboxes, even though the games list hadn't changed. Split intoSyncMonitorThresholds()(called on every textbox change) andSyncMonitorGames()(called only when theGamescollection actually mutates).Version bump
2.0.4 → 2.0.5 in
csproj,Setup.iss,build-installer.ps1.Test plan
IGNORE_TIMER_RESOLUTION. Confirmable via Process Hacker → process properties → "Power throttling: Disabled"SyncMonitorGames)