Skip to content

Commit 16b0416

Browse files
committed
anvil: throttle vblanks when running faster as expected
1 parent c21ff35 commit 16b0416

File tree

1 file changed

+49
-10
lines changed

1 file changed

+49
-10
lines changed

anvil/src/udev.rs

Lines changed: 49 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
use std::{
22
collections::hash_map::HashMap,
33
io,
4+
ops::Not,
45
path::Path,
5-
sync::{atomic::Ordering, Mutex},
6+
sync::{atomic::Ordering, Mutex, Once},
67
time::{Duration, Instant},
78
};
89

@@ -35,7 +36,7 @@ use smithay::{
3536
exporter::gbm::GbmFramebufferExporter,
3637
output::{DrmOutput, DrmOutputManager, DrmOutputRenderElements},
3738
CreateDrmNodeError, DrmAccessError, DrmDevice, DrmDeviceFd, DrmError, DrmEvent, DrmEventMetadata,
38-
DrmNode, DrmSurface, GbmBufferedSurface, NodeType,
39+
DrmEventTime, DrmNode, DrmSurface, GbmBufferedSurface, NodeType,
3940
},
4041
egl::{self, context::ContextPriority, EGLDevice, EGLDisplay},
4142
input::InputEvent,
@@ -636,6 +637,8 @@ struct SurfaceData {
636637
#[cfg(feature = "debug")]
637638
fps_element: Option<FpsElement<MultiTexture>>,
638639
dmabuf_feedback: Option<SurfaceDmabufFeedback>,
640+
last_presentation_time: Option<Time<Monotonic>>,
641+
vblank_throttle_timer: Option<RegistrationToken>,
639642
}
640643

641644
impl Drop for SurfaceData {
@@ -1035,6 +1038,8 @@ impl AnvilState<UdevData> {
10351038
#[cfg(feature = "debug")]
10361039
fps_element,
10371040
dmabuf_feedback,
1041+
last_presentation_time: None,
1042+
vblank_throttle_timer: None,
10381043
};
10391044

10401045
device.surfaces.insert(crtc, surface);
@@ -1190,6 +1195,10 @@ impl AnvilState<UdevData> {
11901195
}
11911196
};
11921197

1198+
if let Some(timer_token) = surface.vblank_throttle_timer.take() {
1199+
self.handle.remove(timer_token);
1200+
}
1201+
11931202
let output = if let Some(output) = self.space.outputs().find(|o| {
11941203
o.user_data().get::<UdevOutputId>()
11951204
== Some(&UdevOutputId {
@@ -1203,8 +1212,15 @@ impl AnvilState<UdevData> {
12031212
return;
12041213
};
12051214

1215+
let Some(frame_duration) = output
1216+
.current_mode()
1217+
.map(|mode| Duration::from_secs_f64(1_000f64 / mode.refresh as f64))
1218+
else {
1219+
return;
1220+
};
1221+
12061222
let tp = metadata.as_ref().and_then(|metadata| match metadata.time {
1207-
smithay::backend::drm::DrmEventTime::Monotonic(tp) => Some(tp),
1223+
smithay::backend::drm::DrmEventTime::Monotonic(tp) => tp.is_zero().not().then_some(tp),
12081224
smithay::backend::drm::DrmEventTime::Realtime(_) => None,
12091225
});
12101226

@@ -1221,18 +1237,41 @@ impl AnvilState<UdevData> {
12211237
(self.clock.now(), wp_presentation_feedback::Kind::Vsync)
12221238
};
12231239

1240+
let vblank_remaining_time = surface.last_presentation_time.map(|last_presentation_time| {
1241+
frame_duration.saturating_sub(Time::elapsed(&last_presentation_time, clock))
1242+
});
1243+
1244+
if let Some(vblank_remaining_time) = vblank_remaining_time {
1245+
if vblank_remaining_time > frame_duration / 2 {
1246+
static WARN_ONCE: Once = Once::new();
1247+
WARN_ONCE.call_once(|| {
1248+
warn!("display running faster than expected, throttling vblanks and disabling HwClock")
1249+
});
1250+
let throttled_time = tp
1251+
.map(|tp| tp.saturating_add(vblank_remaining_time))
1252+
.unwrap_or(Duration::ZERO);
1253+
let throttled_metadata = DrmEventMetadata {
1254+
sequence: seq,
1255+
time: DrmEventTime::Monotonic(throttled_time),
1256+
};
1257+
let timer_token = self
1258+
.handle
1259+
.insert_source(Timer::from_duration(vblank_remaining_time), move |_, _, data| {
1260+
data.frame_finish(dev_id, crtc, &mut Some(throttled_metadata));
1261+
TimeoutAction::Drop
1262+
})
1263+
.expect("failed to register vblank throttle timer");
1264+
surface.vblank_throttle_timer = Some(timer_token);
1265+
return;
1266+
}
1267+
}
1268+
surface.last_presentation_time = Some(clock);
1269+
12241270
let submit_result = surface
12251271
.drm_output
12261272
.frame_submitted()
12271273
.map_err(Into::<SwapBuffersError>::into);
12281274

1229-
let Some(frame_duration) = output
1230-
.current_mode()
1231-
.map(|mode| Duration::from_secs_f64(1_000f64 / mode.refresh as f64))
1232-
else {
1233-
return;
1234-
};
1235-
12361275
let schedule_render = match submit_result {
12371276
Ok(user_data) => {
12381277
if let Some(mut feedback) = user_data.flatten() {

0 commit comments

Comments
 (0)