aboutsummaryrefslogtreecommitdiff
path: root/src/layout.rs
diff options
context:
space:
mode:
authorIvan Molodetskikh <yalterz@gmail.com>2023-09-29 13:12:50 +0400
committerIvan Molodetskikh <yalterz@gmail.com>2023-09-29 13:12:50 +0400
commit404661ed8d9efc60d70547287d81953c1025a95b (patch)
treec9030cf6c08cfeaa32acdd10fbbc303e4a7abaf3 /src/layout.rs
parent73fd286f3494f2cf7388710624d3736a514b9f1f (diff)
downloadniri-404661ed8d9efc60d70547287d81953c1025a95b.tar.gz
niri-404661ed8d9efc60d70547287d81953c1025a95b.tar.bz2
niri-404661ed8d9efc60d70547287d81953c1025a95b.zip
Throttle frame callbacks to once per monitor refresh
Under some circumstances, the compositor can get into a commit-frame callback busy loop with a client. For example, if a client redraws on frame callbacks, but the resulting frame has empty damage (e.g. the damaged part of the client is outside the monitor). Or if the client simply commits with empty damage (looking at you, Firefox). This behavior is compliant with the Wayland specification and with the intended idea of frame callbacks, but causes a lot of unnecessary CPU usage in the client and the compositor. To solve this problem, this commit introduces frame callback throttling. Every surface may only receive a single frame callback in one monitor refresh cycle. If a surface commits resulting in no KMS frame submission, a timer is created, that will fire at the predicted would- be-VBlank time, and send the accumulated frame callbacks. This way, a surface that redraws on frame callbacks will not notice any change in frame callback delivery, if its commits suddenly stop producing KMS updates.
Diffstat (limited to 'src/layout.rs')
-rw-r--r--src/layout.rs15
1 files changed, 11 insertions, 4 deletions
diff --git a/src/layout.rs b/src/layout.rs
index fd05c957..4a272f9d 100644
--- a/src/layout.rs
+++ b/src/layout.rs
@@ -579,11 +579,16 @@ impl<W: LayoutElement> MonitorSet<W> {
}
}
- pub fn send_frame(&self, output: &Output, time: Duration) {
+ pub fn send_frame(
+ &self,
+ output: &Output,
+ time: Duration,
+ should_send: &impl Fn(&SurfaceData) -> bool,
+ ) {
if let MonitorSet::Normal { monitors, .. } = self {
for mon in monitors {
if &mon.output == output {
- mon.workspaces[mon.active_workspace_idx].send_frame(time);
+ mon.workspaces[mon.active_workspace_idx].send_frame(time, should_send);
}
}
}
@@ -1826,10 +1831,12 @@ impl<W: LayoutElement> Workspace<W> {
self.add_window(window, true);
}
- fn send_frame(&self, time: Duration) {
+ fn send_frame(&self, time: Duration, should_send: &impl Fn(&SurfaceData) -> bool) {
let output = self.output.as_ref().unwrap();
for win in self.windows() {
- win.send_frame(output, time, None, |_, _| Some(output.clone()));
+ win.send_frame(output, time, None, |_, states| {
+ should_send(states).then(|| output.clone())
+ });
}
}