1
1
use std:: {
2
2
collections:: hash_map:: HashMap ,
3
3
io,
4
+ ops:: Not ,
4
5
path:: Path ,
5
- sync:: { atomic:: Ordering , Mutex } ,
6
+ sync:: { atomic:: Ordering , Mutex , Once } ,
6
7
time:: { Duration , Instant } ,
7
8
} ;
8
9
@@ -35,7 +36,7 @@ use smithay::{
35
36
exporter:: gbm:: GbmFramebufferExporter ,
36
37
output:: { DrmOutput , DrmOutputManager , DrmOutputRenderElements } ,
37
38
CreateDrmNodeError , DrmAccessError , DrmDevice , DrmDeviceFd , DrmError , DrmEvent , DrmEventMetadata ,
38
- DrmNode , DrmSurface , GbmBufferedSurface , NodeType ,
39
+ DrmEventTime , DrmNode , DrmSurface , GbmBufferedSurface , NodeType ,
39
40
} ,
40
41
egl:: { self , context:: ContextPriority , EGLDevice , EGLDisplay } ,
41
42
input:: InputEvent ,
@@ -636,6 +637,8 @@ struct SurfaceData {
636
637
#[ cfg( feature = "debug" ) ]
637
638
fps_element : Option < FpsElement < MultiTexture > > ,
638
639
dmabuf_feedback : Option < SurfaceDmabufFeedback > ,
640
+ last_presentation_time : Option < Time < Monotonic > > ,
641
+ vblank_throttle_timer : Option < RegistrationToken > ,
639
642
}
640
643
641
644
impl Drop for SurfaceData {
@@ -1035,6 +1038,8 @@ impl AnvilState<UdevData> {
1035
1038
#[ cfg( feature = "debug" ) ]
1036
1039
fps_element,
1037
1040
dmabuf_feedback,
1041
+ last_presentation_time : None ,
1042
+ vblank_throttle_timer : None ,
1038
1043
} ;
1039
1044
1040
1045
device. surfaces . insert ( crtc, surface) ;
@@ -1190,6 +1195,10 @@ impl AnvilState<UdevData> {
1190
1195
}
1191
1196
} ;
1192
1197
1198
+ if let Some ( timer_token) = surface. vblank_throttle_timer . take ( ) {
1199
+ self . handle . remove ( timer_token) ;
1200
+ }
1201
+
1193
1202
let output = if let Some ( output) = self . space . outputs ( ) . find ( |o| {
1194
1203
o. user_data ( ) . get :: < UdevOutputId > ( )
1195
1204
== Some ( & UdevOutputId {
@@ -1203,8 +1212,15 @@ impl AnvilState<UdevData> {
1203
1212
return ;
1204
1213
} ;
1205
1214
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
+
1206
1222
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) ,
1208
1224
smithay:: backend:: drm:: DrmEventTime :: Realtime ( _) => None ,
1209
1225
} ) ;
1210
1226
@@ -1221,18 +1237,41 @@ impl AnvilState<UdevData> {
1221
1237
( self . clock . now ( ) , wp_presentation_feedback:: Kind :: Vsync )
1222
1238
} ;
1223
1239
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
+
1224
1270
let submit_result = surface
1225
1271
. drm_output
1226
1272
. frame_submitted ( )
1227
1273
. map_err ( Into :: < SwapBuffersError > :: into) ;
1228
1274
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
-
1236
1275
let schedule_render = match submit_result {
1237
1276
Ok ( user_data) => {
1238
1277
if let Some ( mut feedback) = user_data. flatten ( ) {
0 commit comments