11use std:: { collections:: VecDeque , num:: NonZeroU64 , time:: Duration } ;
22
3- use smithay:: utils:: { Clock , Monotonic , Time } ;
3+ use smithay:: {
4+ backend:: drm:: DrmNode ,
5+ utils:: { Clock , Monotonic , Time } ,
6+ } ;
47use tracing:: { debug, error} ;
58
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 ;
811
912pub struct Timings {
1013 refresh_interval_ns : Option < NonZeroU64 > ,
1114 min_refresh_interval_ns : Option < NonZeroU64 > ,
1215 vrr : bool ,
16+ vendor : Option < u32 > ,
1317
1418 pub pending_frame : Option < PendingFrame > ,
1519 pub previous_frames : VecDeque < Frame > ,
@@ -37,6 +41,10 @@ impl Frame {
3741 self . render_duration_elements + self . render_duration_draw
3842 }
3943
44+ fn submit_time ( & self ) -> Duration {
45+ Time :: elapsed ( & self . render_start , self . presentation_submitted )
46+ }
47+
4048 fn frame_time ( & self ) -> Duration {
4149 Time :: elapsed ( & self . render_start , self . presentation_presented )
4250 }
@@ -49,6 +57,7 @@ impl Timings {
4957 refresh_interval : Option < Duration > ,
5058 min_interval : Option < Duration > ,
5159 vrr : bool ,
60+ node : DrmNode ,
5261 ) -> Self {
5362 let refresh_interval_ns = if let Some ( interval) = & refresh_interval {
5463 assert_eq ! ( interval. as_secs( ) , 0 ) ;
@@ -64,10 +73,20 @@ impl Timings {
6473 None
6574 } ;
6675
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+
6785 Self {
6886 refresh_interval_ns,
6987 min_refresh_interval_ns,
7088 vrr,
89+ vendor,
7190
7291 pending_frame : None ,
7392 previous_frames : VecDeque :: new ( ) ,
@@ -209,6 +228,22 @@ impl Timings {
209228 / ( self . previous_frames . len ( ) as u32 )
210229 }
211230
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+
212247 pub fn avg_frametime ( & self , window : usize ) -> Option < Duration > {
213248 if self . previous_frames . len ( ) < window || window == 0 {
214249 return None ;
@@ -308,15 +343,28 @@ impl Timings {
308343 }
309344
310345 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+
311353 let estimated_presentation_time = self . next_presentation_time ( clock) ;
312354 if estimated_presentation_time. is_zero ( ) {
313355 return Duration :: ZERO ;
314356 }
315357
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 ) {
317360 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 ) ;
318365 } ;
319366
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)
321369 }
322370}
0 commit comments