emilk / emilk/egui

Drag-resize causes application lock-up

Open
#8,314 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug
Dominant language
Rust
Stars
30.6k
Forks
2.1k
Avg merge
1d 9h
Merged PRs (30d)
72

Description

Describe the bug

Using egui 0.35.0, eframe 0.35.0, winit 0.30.13, calloop 0.13.0 on a Wayland session, drag-resizing (edge/corner) the window causes 100% CPU load on the main thread, and the application locks up. The issue persists after ending the drag-resize interaction. Single-event resizing (for instance, hitting the maximize window button) does not cause this issue.

To Reproduce

Steps to reproduce the behavior:

  1. Any eframe (wgpu) app on a Wayland session. A non-trivial per-frame paint makes it more reliable (a paint that takes a few ms widens the window in which the compositor's frame callback is outstanding).
  2. Grab a window edge or corner and drag to resize
  3. Observe the process's main thread CPU load

Expected behavior

CPU load tracks the resize work and returns to idle when drag stops.

Actual behavior

The main thread remains at 100%. eframe::App::ui stops being invoked. The window does not repaint.

Suspected root cause

1. winit defers RedrawRequested while a frame callback is outstanding

winit-0.30.13/src/platform_impl/linux/wayland/event_loop/mod.rs ~L486:
While the surface is waiting for the compositor's wl_surface.frame callback, winit withholds RedrawRequest. An interactive resize (a storm of configures) leaves that callback outstanding.

2. eframe leaves ControlFlow::Poll when the redraw is deferred

eframe-0.35.0/src/native/run.rs ~L188-L243:
When a repaint is due, eframe sets ControlFlow::Poll, calls request_redraw() and removes the entry from windows_next_repaint_times. It relies on winit emitting RedrawRequested --> run_ui_and_paint() --> the app scheduling the next repaint --> the WaitUntil reschedule at L241.
If winit defers the redraw, run_ui_and_paint never runs, nothing re-populates windows_next_repaint_times, so next_repaint_time is None and ControlFlow is never moved off Poll.

3. The interaction causes a zero-timeout issue

winit-0.30.13/src/platform_impl/linux/wayland/event_loop/mod.rs ~L266:
ControlFlow::Poll --> timeout 0 --> loop_dispatch(0) returns immediately with no new Wayland events --> single_iteration runs (no RedrawRequested emitted) --> back to the top. The loop never blocks --> 100% CPU load.
In contrast a maximize window is a single-configure event whose one frame callback resolves promptly, so the deferral clears in one shot.
A resize drag keeps the callback outstanding, so eframe stays on Poll.

Evidence

egui::Context::repaint_causes() logged from the top of the application's ui closure: Fires on startup, then never again after starting drag-resize.

What does NOT fix it

  • NativeOptions { run_and_return: false, .. } -- No effect. winit's EventLoop:run on the Wayland backend funnels through the same run_on_demand --> pump_events --> poll_events_with_timeout code
  • Application-side mitigation -- Impossible from the application code, ui never runs during drag-resize, so no application code executes to break it, and egui schedules the minimum requested repaint delay, so a background-thread request_repaint_after cannot push the already-pending immediate repaint into the future to forace a WaitUntil.
  • Not the swap chain -- The wgpu surface-error path is not involved (no surface recreation/Outdated churn).

Proposed resolution

Either layer resolves it; the eframe-side change is the smaller and more targeted one:

  • eframe: In check_redraw_requests when a repaint was requested but no future repaint remains scheduled, fall back to a bounded ControlFlow::WaitUntil(now + small) instead of leaving ControlFlow::Poll. A deferred RedrawRequested then blocks on the Wayland fd (waking on the frame callback) instead of busy-spinning, while a normally-serviced redraw is unaffected (the RedrawRequested arrives on the same iteration, well before the small timeout). This turns a 100% CPU lock-up into an at-worst bounded wait.
  • winit: Don't wait indefinitely on a stale frame callback. Reset or time-bound FrameCallbackState::Requested across an interactive drag-resize, so a lost callback can't inhibit RedrawRequested forever.

Related eframe busy-loop guards already exist for the invisible/minimized-window case (INVISIBLE_WINDOW_REPAINT_INTERVALL; see egui issues #7776, #5229). This is the same failure mode for the frame-callback-deferred case during resize.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Reproduce the lock-up in a Rust eframe app on Wayland, then read eframe-0.35.0/src/native/run.rs around check_redraw_requests and winit-0.30.13/src/platform_impl/linux/wayland/event_loop/mod.rs around the redraw and timeout handling. Compare the proposed eframe and winit fixes; done means drag-resizing no longer leaves the main thread at 100% CPU and repainting resumes.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
desktop, operating-systems
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.