ecore-x : move x vsync device back to mainloop from thread
authorCarsten Haitzler (Rasterman) <raster@rasterman.com>
Sat, 23 May 2020 08:28:45 +0000 (09:28 +0100)
committerJongmin Lee <jm105.lee@samsung.com>
Sun, 24 May 2020 21:32:20 +0000 (06:32 +0900)
after i noticed some jitters, it seesms one thread to listen for vsync
events then to wake up the mainloop can suffer from irregular
scheduling jitters. it seems to be highly depenedent on both gpu
driver and cpu but it seemed the vsync thread itself was more reliably
woken than the mainlooop it then signalled, so merging this back is
just batter. it's configurable via an environment variable so we can
try either right now and see, but default is now to be in main loop.

src/lib/ecore_x/ecore_x_vsync.c

index a47e6df21cf6cdd70b8de4e3d4dc7b9c9d9ae077..d9b8529296430ae66af2d4cae78fc2220fc36a6d 100644 (file)
@@ -154,6 +154,9 @@ static Ecore_Thread *drm_thread = NULL;
 static Eina_Spinlock tick_queue_lock;
 static int           tick_queue_count = 0;
 static Eina_Bool     tick_skip = EINA_FALSE;
+static Eina_Bool     threaded_vsync = EINA_FALSE;
+static Ecore_Timer  *fail_timer = NULL;
+static Ecore_Timer  *fallback_timer = NULL;
 
 typedef struct
 {
@@ -167,6 +170,37 @@ typedef struct
 # define D(args...)
 #endif
 
+static void _drm_send_time(double t);
+
+static Eina_Bool
+_fallback_timeout(void *data EINA_UNUSED)
+{
+   if (drm_event_is_busy)
+     {
+        _drm_send_time(ecore_loop_time_get());
+        return EINA_TRUE;
+     }
+   fallback_timer = NULL;
+   return EINA_FALSE;
+}
+
+static Eina_Bool
+_fail_timeout(void *data EINA_UNUSED)
+{
+   fail_timer = NULL;
+   _drm_fail_count++;
+   if (_drm_fail_count >= 10)
+     {
+        _drm_fail_count = 10;
+        if (!fallback_timer)
+          fallback_timer = ecore_timer_add
+            (1.0 / 60.0, _fallback_timeout, NULL);
+     }
+   if (drm_event_is_busy)
+     _drm_send_time(ecore_loop_time_get());
+   return EINA_FALSE;
+}
+
 static Eina_Bool
 _drm_tick_schedule(void)
 {
@@ -196,7 +230,33 @@ _drm_tick_begin(void *data EINA_UNUSED)
 {
    _drm_fail_count = 0;
    drm_event_is_busy = 1;
-   _tick_send(1);
+   if (threaded_vsync)
+     {
+        _tick_send(1);
+     }
+   else
+     {
+        if (fail_timer) ecore_timer_reset(fail_timer);
+        else fail_timer = ecore_timer_add(1.0 / 15.0, _fail_timeout, NULL);
+        if (_drm_fail_count < 10)
+          {
+             if (!_drm_tick_schedule())
+               {
+                  _drm_fail_count = 999999;
+                  if (!fallback_timer)
+                    fallback_timer = ecore_timer_add
+                      (1.0 / 60.0, _fallback_timeout, NULL);
+               }
+          }
+        else
+          {
+             if (!_drm_tick_schedule())
+               _drm_fail_count = 999999;
+             if (!fallback_timer)
+               fallback_timer = ecore_timer_add
+                 (1.0 / 60.0, _fallback_timeout, NULL);
+          }
+     }
 }
 
 static void
@@ -204,22 +264,64 @@ _drm_tick_end(void *data EINA_UNUSED)
 {
    _drm_fail_count = 0;
    drm_event_is_busy = 0;
-   _tick_send(0);
+   if (threaded_vsync)
+     {
+        _tick_send(0);
+     }
+   else
+     {
+        if (fail_timer)
+          {
+             ecore_timer_del(fail_timer);
+             fail_timer = NULL;
+          }
+        if (fallback_timer)
+          {
+             ecore_timer_del(fallback_timer);
+             fallback_timer = NULL;
+          }
+     }
 }
 
 static void
 _drm_send_time(double t)
 {
-   double *tim = malloc(sizeof(*tim));
-   if (tim)
+   if (threaded_vsync)
+     {
+        double *tim = malloc(sizeof(*tim));
+        if (tim)
+          {
+             *tim = t;
+             DBG("   ... send %1.8f", t);
+             D("    @%1.5f   ... send %1.8f\n", ecore_time_get(), t);
+             eina_spinlock_take(&tick_queue_lock);
+             tick_queue_count++;
+             eina_spinlock_release(&tick_queue_lock);
+             ecore_thread_feedback(drm_thread, tim);
+          }
+     }
+   else
      {
-        *tim = t;
-        DBG("   ... send %1.8f", t);
-        D("    @%1.5f   ... send %1.8f\n", ecore_time_get(), t);
-        eina_spinlock_take(&tick_queue_lock);
-        tick_queue_count++;
-        eina_spinlock_release(&tick_queue_lock);
-        ecore_thread_feedback(drm_thread, tim);
+        if (drm_event_is_busy)
+          {
+             if (_drm_fail_count == 0)
+               {
+                  if (fallback_timer)
+                    {
+                       ecore_timer_del(fallback_timer);
+                       fallback_timer = NULL;
+                    }
+               }
+             ecore_loop_time_set(t);
+             ecore_animator_custom_tick();
+             if (drm_event_is_busy)
+               {
+                  if (fail_timer) ecore_timer_reset(fail_timer);
+                  else fail_timer = ecore_timer_add(1.0 / 15.0, _fail_timeout, NULL);
+                  if (!_drm_tick_schedule())
+                    _drm_fail_count = 999999;
+               }
+          }
      }
 }
 
@@ -272,6 +374,7 @@ _drm_vblank_handler(int fd EINA_UNUSED,
                   tdelta_avg = 0.0;
                   tdelta_n = 0;
                }
+             _drm_fail_count = 0;
              _drm_send_time(t);
              pframe = frame;
           }
@@ -428,6 +531,15 @@ _drm_tick_notify(void *data EINA_UNUSED, Ecore_Thread *thread EINA_UNUSED, void
    free(msg);
 }
 
+static Eina_Bool
+_ecore_vsync_fd_handler(void *data EINA_UNUSED,
+                        Ecore_Fd_Handler *fd_handler EINA_UNUSED)
+{
+   _ecore_x_vsync_wakeup_time = ecore_time_get();
+   sym_drmHandleEvent(drm_fd, &drm_evctx);
+   return ECORE_CALLBACK_RENEW;
+}
+
 // yes. most evil. we dlopen libdrm and libGL etc. to manually find smbols
 // so we can be as compatible as possible given the whole mess of the
 // gl/dri/drm etc. world. and handle graceful failure at runtime not
@@ -701,11 +813,20 @@ checkdone:
      }
 
    if (getenv("ECORE_ANIMATOR_SKIP")) tick_skip = EINA_TRUE;
-   tick_queue_count = 0;
-   eina_spinlock_new(&tick_queue_lock);
-   thq = eina_thread_queue_new();
-   drm_thread = ecore_thread_feedback_run(_drm_tick_core, _drm_tick_notify,
-                                          NULL, NULL, NULL, EINA_TRUE);
+   if (threaded_vsync)
+     {
+        tick_queue_count = 0;
+        eina_spinlock_new(&tick_queue_lock);
+        thq = eina_thread_queue_new();
+        drm_thread = ecore_thread_feedback_run(_drm_tick_core, _drm_tick_notify,
+                                               NULL, NULL, NULL, EINA_TRUE);
+     }
+   else
+     {
+        ecore_main_fd_handler_add(drm_fd, ECORE_FD_READ,
+                                  _ecore_vsync_fd_handler, NULL,
+                                  NULL, NULL);
+     }
    return 1;
 }
 
@@ -785,6 +906,7 @@ ecore_x_vsync_animator_tick_source_set(Ecore_X_Window win)
         const char *home;
         struct stat st;
 
+        if (getenv("ECORE_VSYNC_TRHEAD")) threaded_vsync = EINA_TRUE;
         home = eina_environment_home_get();
         if (!home) eina_environment_tmp_get();
         snprintf(buf, sizeof(buf), "%s/.ecore-no-vsync", home);