Repository navigation
Fix system-wide mouse lag and CPU usage from the edge-light overlay - #214
bruceroberts-mantel wants to merge 4 commits into
Conversation
Throttle/coalesce the global mouse hook and settings-slider ValueChanged events so rapid input no longer floods the UI thread with redundant work. Cache the blurred frame's rendered bitmap and reduce the drop shadow blur radius to cut per-update rendering cost. Add temporary perf-log instrumentation used to diagnose the sluggishness. Confirmed working and responsive; still noticeably CPU-heavy under the current AllowsTransparency-based rendering model. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The overlay's AllowsTransparency WPF window forced a full re-render and re-transfer of its entire monitor-sized surface on every visual change, however small - brightness, color temperature, and cursor-hover updates all paid that cost. Replace it with NativeLayeredWindow, a raw Win32 layered window managed independently of WPF, fed by GlowBitmapRenderer (off-screen WPF rendering, cached as raw pixel bytes). Brightness now uses UpdateLayeredWindow's own alpha-blend parameter - no re-render at all. Hover updates punch the cursor hole directly into the cached pixel bytes instead of re-running WPF's render pipeline (which measured 100-300ms per call depending on monitor DPI), so moving the cursor near the frame no longer pins a CPU core. The main window itself shrinks to an inert 1x1px shell (hotkeys/mouse hook/DPI tracking only) since a monitor-sized WPF window - even with empty content - was independently making cursor movement sluggish across that whole screen. Also remove the temporary PerfLog instrumentation used to diagnose this. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Color-temperature changes (and resize/monitor-switch/toggle) were blocking the main UI thread during WPF's off-screen render, which also owns the global mouse hook - Windows expects that hook to return promptly, so the block was showing up as system-wide mouse lag, not just an unresponsive app. GlowRenderWorker moves that render onto a dedicated thread with its own WPF Dispatcher; results are applied back on the UI thread once ready, with a request-id check so a stale in-flight render can't clobber a newer one. The Settings window shows an "Applying..." indicator next to the color-temperature slider while a change is still rendering, since it's no longer instant. Also: - Listen for Microsoft.Win32.SystemEvents.DisplaySettingsChanged and re-run monitor layout/geometry on any OS-level display change (monitor added/removed, resolution changed) - previously only this window's own size/position/DPI changes triggered a re-layout, which stopped happening once MainWindow became a fixed-size shell. - Widen BlurRadius (76 -> 140) and MarginInset (40 -> 70) so the soft edge spans more physical pixels. Verified via direct alpha-channel sampling that the previous radius produced a smooth gradient in the rendered bitmap but only ~20px wide on lower-DPI monitors - narrow enough that some display paths (adapter/monitor color processing) were crushing it into a hard edge despite the source data being correct on both ends. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
Automated review follow-up from Copilot, acting on Scott's behalf. Thanks for digging into this. There is a real hot path in the current design: mouse movement is posted through the WPF dispatcher while a monitor-sized transparent window is being updated, and moving toward explicitly managed layered windows is a promising direction. The working-set improvement in particular is substantial. I am not comfortable merging the current version yet, because focused review and local ARM64 measurements found two blocking correctness/performance issues:
The native ARM64 measurements on a 2180×1405 working area were:
So the memory reduction is real and valuable, but color-temperature dragging currently consumes roughly 6.5× the CPU of Before another review, could you please:
This PR also combines the renderer rewrite with a much larger blur/look change, display hotplug behavior, and Settings UX changes. Please separate the visual/blur and display-management changes from the renderer work—or at minimum split them into independently reviewable commits—so we can evaluate the performance fix without unrelated visual behavior moving at the same time. The Release build succeeds cleanly, and the overall direction is worth continuing. Thank you for taking on a difficult area and for the significant memory improvement; the remaining request is to make the lifecycle safe and demonstrate that the CPU/input path improves rather than moving the cost elsewhere. |
Replace the WH_MOUSE_LL hook with a 33 ms GetCursorPos poll that only runs while the light is on, so other apps' mouse input never waits on our UI thread. Cache the blurred glow (BitmapCache) and apply brightness and the hover hole outside the cache, so they no longer re-run the full-screen blur. Maintainer patch based on the investigation in #214. Co-authored-by: bruceroberts-mantel <269349613+bruceroberts-mantel@users.noreply.github.com> Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
|
Hi @bruceroberts-mantel! I'm a Copilot triage agent helping maintain this repo (not Scott himself). Thank you for digging into this. You correctly found that the global mouse hook and the overlay redraws were behind the lag and CPU use, and that investigation was really helpful. We ended up landing a smaller maintainer patch in #220 that takes a different route. It removes the low-level mouse hook entirely and polls the cursor only while the light is on. It also caches the blurred glow so brightness and the cursor hole don't re-run the blur. That kept the change small and avoided some side effects we hit while testing this branch, like a GDI leak and a slowdown during color changes. You're credited as a co-author on that commit. I'm leaving this PR open so you can decide what to do with it. If anything from your approach still seems worth adding, a follow-up would be welcome. Thanks again! |
|
Thanks for the thoughtful investigation and the substantial work on this. Your findings helped us identify the mouse-hook and rendering costs, and informed the smaller fix that shipped in #220. Since #220 addresses the reported issue with a simpler approach, this PR's implementation is no longer needed, so I'm closing it. We really appreciate your contribution. |
Summary
color-temperature changes and while hovering the cursor near the glow
rendered off the main UI thread, substantially reducing CPU usage
changes, displays connected/disconnected) instead of requiring a restart
with different DPI scaling and display characteristics
still rendering
Why
The previous implementation relied on a WPF
AllowsTransparencywindow, which forces afull re-render and re-transfer of the entire monitor-sized surface on every visual
change, however small. Combined with a global low-level mouse hook sharing the same UI
thread, this caused input lag across the whole system (not just the app) during normal
interaction - most severely during color-temperature changes, which could stall mouse
movement system-wide for as long as the render took.
Test plan
additional monitors
🤖 Generated with Claude Code