Conversation
Windows ask the platform for a frame when they need one while the platform frame loop is idle. A no-op on platforms whose loop runs continuously while visible.
The frame callback loop re-armed itself with a commit on every frame, waking the process at refresh rate even when nothing changed. Now a frame only re-arms when something was drawn or another frame is wanted, and request_redraw resumes the parked loop. Also keeps a forced render alive when the frame it was requested for got throttled.
The explicit `completed_frame` was a code smell and only existed to execute Wayland's frame-end bookkeeping, it's now inlined in `schedule_frame` so that becomes the entire render loop contract. Takes some elements from #60308 and test instrumentation from #61063. Co-authored-by: Philipp Schaffrath <philipp.schaffrath@gmail.com> Co-authored-by: Daan De Meyer <daan@amutable.com>
I found the exact opposite behaviour: when there is nothing to stitch for the compositor, and the Wayland client doesn't draw, the Wayland server does not invoke the app callbacks to draw, and so the drawing freezes completely. So 0 draw callbacks are called; nothing is rendered on the screen. This is why #60690 was created, as well as why I created my PR. Does it work here the same way? Cuz judging by the description, it attempts to solve a different problem. This behaviour also isn't specific to GPUI and Zed; I ran into it myself when I was also recently creating a game ui in Vulkan and found the exact same issue with Wayland. |
|
@iddm Before diving into anything, have you tested if this PR solves your problem? Compiling and testing is a bit faster than trying to figure out what causes your problem, understanding that problem, and finding out if this approach fixes it by chance. Please give it a quick test, and let me know which compositor you are experiencing that issue under. |
Yes, I wanted to test it, but didn't have time today, hopefully, tomorrow. I am on Hyprland. |
Let me know once you had some time to test! Thanks for the help! |
|
Thank you for working on this! Your debug of the issue is spot on; the PR that @iddm referenced tackles the same thing but also gets rid of the bare surface commit: #60690 The difference is whether we rely on compositor wakes or handle that internally, and this PR uses the existing mechanism (the former) but this causes issues on a fullscreen because callbacks only fire when the compositor repaints, and a damageless (i.e. no change) commit on a static surface may never get one. I've added you as a co-author on #60690, so your contribution will still be reflected. The remaining gaps you identified (broader flush wake, async However, I'm happy to wait on testing before moving forward in case it turns out we're mistaken about the bufferless commit being the root cause. |
|
@NeelChotai thanks for taking over! 👍 Since @iddm already confirmed (#60690 (comment)) that their problem is resolved in the new PR, we should just go ahead with the merge instead of waiting to see if this now obsolete PR also solves it. Closing this PR in favor of #60690 |
I am sorry for the delay. Legitimately tried to run, but rustc, for some reason, eats out all memory... could not launch it to confirm. |
The explicit `completed_frame` was a code smell and only existed to execute Wayland's frame-end bookkeeping, it's now inlined in `schedule_frame` so that becomes the entire render loop contract. Takes some elements from #60308 and test instrumentation from #61063. Co-authored-by: Philipp Schaffrath <philipp.schaffrath@gmail.com> Co-authored-by: Daan De Meyer <daan@amutable.com>
The explicit `completed_frame` was a code smell and only existed to execute Wayland's frame-end bookkeeping, it's now inlined in `schedule_frame` so that becomes the entire render loop contract. Takes some elements from #60308 and test instrumentation from #61063. Co-authored-by: Philipp Schaffrath <philipp.schaffrath@gmail.com> Co-authored-by: Daan De Meyer <daan@amutable.com>
The explicit `completed_frame` was a code smell and only existed to execute Wayland's frame-end bookkeeping, it's now inlined in `schedule_frame` so that becomes the entire render loop contract. Takes some elements from #60308 and test instrumentation from #61063. Co-authored-by: Philipp Schaffrath <philipp.schaffrath@gmail.com> Co-authored-by: Daan De Meyer <daan@amutable.com> Co-authored-by: Christopher Biscardi <chris@christopherbiscardi.com>
The explicit `completed_frame` was a code smell and only existed to execute Wayland's frame-end bookkeeping, it's now inlined in `schedule_frame` so that becomes the entire render loop contract. Takes some elements from #60308 and test instrumentation from #61063. Co-authored-by: Philipp Schaffrath <philipp.schaffrath@gmail.com> Co-authored-by: Daan De Meyer <daan@amutable.com> Co-authored-by: Christopher Biscardi <chris@christopherbiscardi.com>
The explicit `completed_frame` was a code smell and only existed to execute Wayland's frame-end bookkeeping, it's now inlined in `schedule_frame` so that becomes the entire render loop contract. Takes some elements from #60308 and test instrumentation from #61063. Co-authored-by: Philipp Schaffrath <philipp.schaffrath@gmail.com> Co-authored-by: Daan De Meyer <daan@amutable.com> Co-authored-by: Christopher Biscardi <chris@christopherbiscardi.com>
The explicit `completed_frame` was a code smell and only existed to execute Wayland's frame-end bookkeeping, it's now inlined in `schedule_frame` so that becomes the entire render loop contract. Takes some elements from #60308 and test instrumentation from #61063. Co-authored-by: Philipp Schaffrath <philipp.schaffrath@gmail.com> Co-authored-by: Daan De Meyer <daan@amutable.com> Co-authored-by: Christopher Biscardi <chris@christopherbiscardi.com>
Objective
GPUI apps on Wayland burn CPU at the display refresh rate while a window is visible (at least partially on an active output), even when nothing changes. The frame callback loop unconditionally re-arms itself: every
donerequests the next frame callback and ends in a commit, so the compositor wakes us ~60 times (or more on higher refresh rates) per second forever, only to skip drawing.I noticed this while working on a GPUI application, and found an empty window constantly consuming CPU resources.
Solution
PlatformWindow::request_redraw(), a no-op everywhere except Wayland.request_redraw, which resumes a parked loop with one bare commit. All state changes funnel through the effect flush, so no wake source is missed.Testing
Measured debug build: an idle window went from 62 wakeups/s and ~2% CPU to 0 wakeups and 0% over 20s,
WAYLAND_DEBUGshows the protocol going silent. Verified redraw-on-timer (park, wake, redraw, re-park),continuous animation at full rate.
X11 is unaffected (tested on bspwm), and
cargo test -p gpuiandscript/clippystill pass.To test: run any gpui example on Wayland and watch CPU in htop (or any other system monitor).
Self-Review Checklist:
Release Notes: