@@ -87,6 +87,21 @@ struct sgi_video_sync_vblank_scheduler {
8787 pthread_t sync_thread ;
8888 bool running , error , vblank_requested ;
8989 unsigned int last_msc ;
90+ /// Number of synthesized vblank event. Currently these are being inserted when
91+ /// the driver reports duplicate msc to us.
92+ ///
93+ /// This is because the NVIDIA driver has been observed to recover from a
94+ /// duplicated msc loop by us rendering a new frame. So we insert a fake vblank
95+ /// event so the render loop can progress, but doing so makes our msc desync from
96+ /// the driver's msc. An hypothetical sequence of events go like this:
97+ ///
98+ /// - driver -> msc 1, we report msc 1
99+ /// - driver -> msc 1 (duplicate msc! if we don't render a new frame, we'll
100+ /// be stuck here forever)
101+ /// - we report msc 2
102+ /// - new frame rendered, driver recover
103+ /// - driver -> msc 2, but we already reported msc 2, so we have to report msc 3
104+ unsigned int vblank_inserted ;
90105
91106 /// Protects `running`, and `vblank_requested`
92107 pthread_mutex_t vblank_requested_mtx ;
@@ -199,6 +214,8 @@ static void *sgi_video_sync_thread(void *data) {
199214 pthread_cond_signal (& args -> start_cnd );
200215 pthread_mutex_unlock (& args -> start_mtx );
201216
217+ unsigned int last_msc = 0 ;
218+
202219 pthread_mutex_lock (& self -> vblank_requested_mtx );
203220 while (self -> running ) {
204221 if (!self -> vblank_requested ) {
@@ -208,10 +225,9 @@ static void *sgi_video_sync_thread(void *data) {
208225 }
209226 pthread_mutex_unlock (& self -> vblank_requested_mtx );
210227
211- unsigned int last_msc ;
212228 glXWaitVideoSyncSGI (1 , 0 , & last_msc );
213229
214- struct timespec now ;
230+ struct timespec now = {} ;
215231 clock_gettime (CLOCK_MONOTONIC , & now );
216232 atomic_store (& self -> current_msc , last_msc );
217233 atomic_store (& self -> current_ust ,
@@ -323,28 +339,33 @@ static void sgi_video_sync_scheduler_deinit(struct vblank_scheduler *base) {
323339static void
324340sgi_video_sync_scheduler_callback (EV_P attr_unused , ev_async * w , int attr_unused revents ) {
325341 auto sched = container_of (w , struct sgi_video_sync_vblank_scheduler , notify );
326- auto msc = atomic_load (& sched -> current_msc );
327- if (sched -> last_msc == msc ) {
342+ auto msc = atomic_load (& sched -> current_msc ) + sched -> vblank_inserted ;
343+ auto ust = atomic_load (& sched -> current_ust );
344+ if (sched -> last_msc >= msc ) {
328345 // NVIDIA spams us with duplicate vblank events after a suspend/resume
329- // cycle. Recreating the X connection and GLX context seems to fix this.
330- // Oh NVIDIA.
331- log_warn ("Duplicate vblank event found with msc %d. Possible NVIDIA bug?" , msc );
332- log_warn ("Resetting the vblank scheduler" );
333- sgi_video_sync_scheduler_deinit (& sched -> base );
334- sched -> base .vblank_event_requested = false;
335- if (!sgi_video_sync_scheduler_init (& sched -> base )) {
336- log_error ("Failed to reset the vblank scheduler" );
337- } else {
338- sgi_video_sync_scheduler_schedule (& sched -> base );
339- }
340- return ;
346+ // cycle, or when the monitor turns off.
347+ // Fake a vblank event in this case. See comments on `vblank_inserted`
348+ // for more details.
349+ enum log_level level =
350+ sched -> vblank_inserted == 0 ? LOG_LEVEL_WARN : LOG_LEVEL_DEBUG ;
351+ LOG_ (level ,
352+ "Duplicate vblank event found with msc %d. Possible NVIDIA bug? "
353+ "Number of duplicates so far: %d" ,
354+ msc , sched -> vblank_inserted );
355+ sched -> last_msc ++ ;
356+ sched -> vblank_inserted ++ ;
357+
358+ struct timespec now ;
359+ clock_gettime (CLOCK_MONOTONIC , & now );
360+ ust = (uint64_t )(now .tv_sec * 1000000 + now .tv_nsec / 1000 );
361+ } else {
362+ sched -> last_msc = msc ;
341363 }
342364 auto event = (struct vblank_event ){
343- .msc = msc ,
344- .ust = atomic_load ( & sched -> current_ust ) ,
365+ .msc = sched -> last_msc ,
366+ .ust = ust ,
345367 };
346368 sched -> base .vblank_event_requested = false;
347- sched -> last_msc = msc ;
348369 log_verbose ("Received vblank event for msc %" PRIu64 , event .msc );
349370 vblank_scheduler_invoke_callbacks (& sched -> base , & event );
350371}
0 commit comments