Conversation
|
We require contributors to sign our Contributor License Agreement, and we don't have @railapex on file. You can sign our CLA at https://zed.dev/cla. Once you've signed, post a comment here that says '@cla-bot check'. |
|
@cla-bot check |
|
We require contributors to sign our Contributor License Agreement, and we don't have @railapex on file. You can sign our CLA at https://zed.dev/cla. Once you've signed, post a comment here that says '@cla-bot check'. |
|
The cla-bot has been summoned, and re-checked this pull request! |
|
@cla-bot check |
|
The cla-bot has been summoned, and re-checked this pull request! |
|
Thanks! The issue here is valid, but this fix doesn't seem to be the right one. I've labelled the relevant issue as zed-team-only as I believe we need to take this on |
Objective
Fixes #61469.
On Windows, GPUI can keep processing queued work while a visible window stops presenting. The vsync thread invalidates the window, but Windows synthesizes
WM_PAINTonly after higher-priority sent, posted, and input work has drained. A continuously busy posted queue can therefore starve both pending paint and hardware input.The original report uses held-key input. #61632 demonstrates the same synthesized-paint mechanism with high-polling mouse input. In the controlled matrix below, isolated 60 Hz key and 1 kHz mouse input are healthy on the frozen base. Saturated posted traffic is the deterministic trigger: it starves paint, and when combined with 1 kHz mouse motion it also starves the input queue. A keydown-only draw request fixes one symptom but leaves the queue-level failure.
Solution
After dispatching a normally retrieved message, the outer Windows message loop runs a bounded fairness probe at most once per millisecond:
GetMessageWpath.The one-millisecond interval is shorter than a 240 Hz frame period and caps an idle queue at 1,000 supplemental probe batches per second. Four input messages per batch gives a 1 kHz device headroom when the outer loop does not regain control exactly once per millisecond. The queued messages selected by each probe stay finite, so the fairness policy does not add an unbounded queue drain. As documented by Win32,
PeekMessageWcan dispatch pending nonqueued sent messages before returning; the quota is not a hard bound on callbacks or CPU work. This was measured at 1 kHz; it is not a claim about higher input rates, and the policy has an idealized selection ceiling of roughly 4,000 input messages per second under simultaneous posted saturation.Both filtered paths retain the normal accelerator, translation, dispatch, and
WM_QUIThandling. The change does not callUpdateWindow, draw synchronously from an input/vsync thread, post another per-vsync signal into the busy FIFO, or change GPUI's invalidation and request-frame path.This follows established message-pump practice rather than introducing a new Win32 primitive. Microsoft documents both the normal queue ordering and class-filtered retrieval with
PM_QS_PAINTandPM_QS_INPUT. GPUI's existingrun_foreground_taskpump already selects those two classes. SDL's current Windows pump independently uses a one-millisecond deadline plus a finite new-message allowance to prevent an endless drain, while Chromium interleaves bounded native-message and application-work turns to prevent posted work from starving paint and timers. The exact one-paint/four-input weighting here is the GPUI-specific policy established by the matrix below.Sources: Win32
PeekMessageW, SDL Windows message pump, and Chromium's Windows message pump.Testing
The source-only deterministic Windows matrix harness contains the exact six-phase fixture, isolated build stager, randomized runner, integrity gates, methodology, and reference aggregate. It excludes binaries, symbols, ETLs, raw logs, machine inventory, and capture-machine privacy policy.
The external Windows fixture keeps animation demand constant and records GPUI platform-draw attempts after message delivery. Each row ran three times in a rotated order against the same frozen base and byte-identical profiler fixture. All 12 runs passed their provenance, foreground, input-restoration, exit, and process-cleanup integrity checks.
Median rates for the deterministic saturated-posted phases:
The paint-only row is intentional: it shows that selecting paint alone can restore frame cadence while the same posted queue still blocks hardware input. This change was the only row to pass all 22 predeclared presentation and input gates in all three runs.
For this change, the worst mouse-plus-posted maximum gap was 9.196 ms and the worst posted-only maximum gap was 11.354 ms. One key-plus-posted run recorded a 76.538 ms maximum gap; the corresponding control phases also had maxima up to 77.115 ms, so the data does not support claiming that every isolated gap stayed below one frame period. The rate, mean, p99, input, and integrity gates passed.
The profiler event is an internal GPUI platform-draw-attempt oracle. It does not prove that
IDXGISwapChain::Presentran or succeeded. Matched ETW/PresentMon captures are the external DXGI check.A single clean-boot A/C/D/I run produced four complete, zero-loss scheduler ETLs plus matching PresentMon DXGI data. Rates are presents/input dispatch per second; parentheses show the maximum present gap:
Frozen main, the narrow key fix, and rebased #61632 each failed the predeclared behavior gates. This exact PR source passed every integrity, presentation, and input gate. The ETLs and matching symbols remain private and can be supplied to maintainers after manual privacy review.
Supplemental checks:
Validation at the PR head:
cargo fmt --all -- --checkcargo check --locked -p gpui --example present_starvationcargo test --locked -p gpui_windows --features test-support— 9 passedscript/clippy -p gpui_windows -p gpuiusing the repository's canonical release/all-target/all-feature configurationBare
cargo test -p gpui_windowshas an existing feature mismatch on unchanged main: crate-localcfg(test)exposesrender_to_image, while the dependency build ofgpuihides the corresponding trait method unlesstest-supportis enabled. This PR does not change that test configuration.Limits
run_foreground_taskhas its own existing local pump. It already selects one paint, drains input, and posts a task-dispatched message to return control; changing that separate reentrant path is not needed for Window stops presenting for seconds under sustained keyboard input (WM_PAINT starvation; dispatch_key_event draws without presenting) #61469 and would enlarge the regression surface.WM_KEYDOWNcan postWM_CHARbehind an existing posted backlog. This change does not promise keydown/character adjacency during an artificial saturated posted flood.WM_KEYDOWNplusWM_NULL; hardware-input fairness is verified by the externalSendInputmatrix rather than asserted by the example.Related work
WM_PAINTstarvation. Its custom per-vsync posted message can make progress during pure input load because posted work outranks input, then callsUpdateWindow. Under a saturated posted FIFO, that signal queues behind the traffic causing starvation. The table reports both approaches under the same workloads; it is not a general performance comparison outside this queue shape.WM_PAINTor hardware input progress through a continuously busy posted queue, so it is not folded into this delivery fix.Self-Review Checklist:
Release Notes: