1
1
use std:: { collections:: VecDeque , num:: NonZeroU64 , time:: Duration } ;
2
2
3
- use smithay:: utils:: { Clock , Monotonic , Time } ;
3
+ use smithay:: {
4
+ backend:: drm:: DrmNode ,
5
+ utils:: { Clock , Monotonic , Time } ,
6
+ } ;
4
7
use tracing:: { debug, error} ;
5
8
6
- const FRAME_TIME_BUFFER : Duration = Duration :: from_millis ( 1 ) ;
7
- const FRAME_TIME_WINDOW : usize = 3 ;
9
+ const BASE_SAFETY_MARGIN : Duration = Duration :: from_millis ( 3 ) ;
10
+ const SAMPLE_TIME_WINDOW : usize = 5 ;
8
11
9
12
pub struct Timings {
10
13
refresh_interval_ns : Option < NonZeroU64 > ,
11
14
min_refresh_interval_ns : Option < NonZeroU64 > ,
12
15
vrr : bool ,
16
+ vendor : Option < u32 > ,
13
17
14
18
pub pending_frame : Option < PendingFrame > ,
15
19
pub previous_frames : VecDeque < Frame > ,
@@ -37,6 +41,10 @@ impl Frame {
37
41
self . render_duration_elements + self . render_duration_draw
38
42
}
39
43
44
+ fn submit_time ( & self ) -> Duration {
45
+ Time :: elapsed ( & self . render_start , self . presentation_submitted )
46
+ }
47
+
40
48
fn frame_time ( & self ) -> Duration {
41
49
Time :: elapsed ( & self . render_start , self . presentation_presented )
42
50
}
@@ -49,6 +57,7 @@ impl Timings {
49
57
refresh_interval : Option < Duration > ,
50
58
min_interval : Option < Duration > ,
51
59
vrr : bool ,
60
+ node : DrmNode ,
52
61
) -> Self {
53
62
let refresh_interval_ns = if let Some ( interval) = & refresh_interval {
54
63
assert_eq ! ( interval. as_secs( ) , 0 ) ;
@@ -64,10 +73,20 @@ impl Timings {
64
73
None
65
74
} ;
66
75
76
+ let vendor = if let Ok ( vendor) = std:: fs:: read_to_string ( format ! (
77
+ "/sys/class/drm/renderD{}/device/vendor" ,
78
+ node. minor( )
79
+ ) ) {
80
+ u32:: from_str_radix ( & vendor. trim ( ) [ 2 ..] , 16 ) . ok ( )
81
+ } else {
82
+ None
83
+ } ;
84
+
67
85
Self {
68
86
refresh_interval_ns,
69
87
min_refresh_interval_ns,
70
88
vrr,
89
+ vendor,
71
90
72
91
pending_frame : None ,
73
92
previous_frames : VecDeque :: new ( ) ,
@@ -209,6 +228,22 @@ impl Timings {
209
228
/ ( self . previous_frames . len ( ) as u32 )
210
229
}
211
230
231
+ pub fn avg_submittime ( & self , window : usize ) -> Option < Duration > {
232
+ if self . previous_frames . len ( ) < window || window == 0 {
233
+ return None ;
234
+ }
235
+
236
+ Some (
237
+ self . previous_frames
238
+ . iter ( )
239
+ . rev ( )
240
+ . take ( window)
241
+ . map ( |f| f. submit_time ( ) )
242
+ . try_fold ( Duration :: ZERO , |acc, x| acc. checked_add ( x) ) ?
243
+ / ( window. min ( self . previous_frames . len ( ) ) as u32 ) ,
244
+ )
245
+ }
246
+
212
247
pub fn avg_frametime ( & self , window : usize ) -> Option < Duration > {
213
248
if self . previous_frames . len ( ) < window || window == 0 {
214
249
return None ;
@@ -308,15 +343,28 @@ impl Timings {
308
343
}
309
344
310
345
pub fn next_render_time ( & self , clock : & Clock < Monotonic > ) -> Duration {
346
+ let Some ( refresh_interval) = self . refresh_interval_ns else {
347
+ return Duration :: ZERO ; // we don't know what to expect, so render immediately.
348
+ } ;
349
+
350
+ const MIN_MARGIN : Duration = Duration :: from_millis ( 3 ) ;
351
+ let baseline = MIN_MARGIN . max ( Duration :: from_nanos ( refresh_interval. get ( ) / 2 ) ) ;
352
+
311
353
let estimated_presentation_time = self . next_presentation_time ( clock) ;
312
354
if estimated_presentation_time. is_zero ( ) {
313
355
return Duration :: ZERO ;
314
356
}
315
357
316
- let Some ( avg_frametime) = self . avg_frametime ( FRAME_TIME_WINDOW ) else {
358
+ // HACK: Nvidia returns `page_flip`/`commit` early, so we have no information to optimize latency on submission.
359
+ if self . vendor == Some ( 0x10de ) {
317
360
return Duration :: ZERO ;
361
+ }
362
+
363
+ let Some ( avg_submittime) = self . avg_submittime ( SAMPLE_TIME_WINDOW ) else {
364
+ return estimated_presentation_time. saturating_sub ( baseline + BASE_SAFETY_MARGIN ) ;
318
365
} ;
319
366
320
- estimated_presentation_time. saturating_sub ( avg_frametime + FRAME_TIME_BUFFER )
367
+ let margin = avg_submittime + BASE_SAFETY_MARGIN ;
368
+ estimated_presentation_time. saturating_sub ( margin)
321
369
}
322
370
}
0 commit comments