-
Notifications
You must be signed in to change notification settings - Fork 53
Windows: Severe input lag (~15s) after window regains focus due to working set trimming #191
Description
Bug Description
On Windows, OpenBubbles becomes extremely laggy (10-15 seconds of input lag) every time the window regains focus after being in the background. Typing, scrolling, and all UI interactions freeze. The app fixes itself after ~15 seconds, but the lag returns every time focus is lost and regained.
This happens on high-end hardware (RTX 3080 Ti, 64GB RAM, NVMe SSD) and is not a resource limitation.
Root Cause
Windows aggressively trims the working set (resident memory) of unfocused applications. When OpenBubbles loses focus, its ~400-500MB working set gets paged out. When focus returns, the OS spends ~15 seconds faulting all those pages back into RAM before the app becomes responsive.
The core issue is in lib/main.dart — the DesktopWindowListener calls ls.close() on onWindowBlur() and ls.open() on onWindowFocus(), and lifecycle_service.dart sets windowFocused = false on close. This signals to the OS that the app is inactive, compounding the trimming.
Additionally:
- The default
logLevelis 4 (INFO), which logs every single keystroke and text prediction to disk. For example, typing the word "test" produces 8+ log lines recording each character and annotation change. Over a typical session this creates tens of megabytes of log I/O, adding constant disk pressure. highPerfModedefaults tofalsekeepAppAlivehas no effect on desktop — it only controls Android foreground services
Suggested Fix
In the Windows runner (windows/runner/flutter_window.cpp or win32_window.cpp), handle WM_ACTIVATE to call SetProcessWorkingSetSizeEx with a hard minimum floor:
// In FlutterWindow::MessageHandler or Win32Window::MessageHandler
case WM_ACTIVATE: {
// Prevent Windows from trimming our working set when losing focus
SIZE_T minSize = 512 * 1024 * 1024; // 512 MB minimum
SIZE_T maxSize = 1536 * 1024 * 1024; // 1.5 GB maximum
SetProcessWorkingSetSizeEx(
GetCurrentProcess(), minSize, maxSize,
QUOTA_LIMITS_HARDWS_MIN_ENABLE);
if (child_content_ != nullptr) {
SetFocus(child_content_);
}
return 0;
}Alternatively, this could be done once at startup in main.cpp after the window is created.
Other Improvements
- Default
logLevelshould be 2 (ERROR) or 3 (WARN) on desktop, not 4 (INFO) — logging every keystroke and text prediction annotation is extremely wasteful and generates tens of MB of logs per session - Consider making
highPerfModedefault totrueon desktop — desktop users rarely need inline media rendering throttled
Workaround
Users can work around this by running the following PowerShell after OpenBubbles starts:
Add-Type -TypeDefinition 'using System; using System.Runtime.InteropServices; public class WS { [DllImport("kernel32.dll", SetLastError=true)] public static extern bool SetProcessWorkingSetSizeEx(IntPtr h, IntPtr min, IntPtr max, uint flags); public const uint HARD_MIN = 0x1; }'
$proc = Get-Process bluebubbles_app
$proc.PriorityClass = [System.Diagnostics.ProcessPriorityClass]::AboveNormal
[WS]::SetProcessWorkingSetSizeEx($proc.Handle, [IntPtr](512MB), [IntPtr]([long]1536*1024*1024), [WS]::HARD_MIN)Environment
- OS: Windows 11
- OpenBubbles version: 1.18.800.0 (Microsoft Store)
- Hardware: RTX 3080 Ti, 64GB RAM, NVMe SSD
- Process stats: ~470MB working set, 461k DB entries in ObjectBox/LMDB