Skip to content

Windows: Severe input lag (~15s) after window regains focus due to working set trimming #191

@richardpowellus

Description

@richardpowellus

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 logLevel is 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.
  • highPerfMode defaults to false
  • keepAppAlive has 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

  1. Default logLevel should 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
  2. Consider making highPerfMode default to true on 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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions