From b0d4793d6fdbc1e7568ab9f913144fc711a9d312 Mon Sep 17 00:00:00 2001 From: "Carsten Haitzler (Rasterman)" Date: Sat, 2 Aug 2014 12:03:35 +0900 Subject: [PATCH] ecore-x-vsync - rearragnge to move vsync event input to thread this uses a thread to collect vsync input events and filter them before forwarding them to the mainloop (as a double timestamp). this means wakeups only happen for the actual vsync and thus animator and not for other screens we are filtering out anyway. this should fix the continual animator wakeups that happen if you have a dri/drm based driver and > 1 screen. --- src/lib/ecore_x/xlib/ecore_x_vsync.c | 213 ++++++++++++++++----------- 1 file changed, 129 insertions(+), 84 deletions(-) diff --git a/src/lib/ecore_x/xlib/ecore_x_vsync.c b/src/lib/ecore_x/xlib/ecore_x_vsync.c index dbc32c039c..eadf41c416 100644 --- a/src/lib/ecore_x/xlib/ecore_x_vsync.c +++ b/src/lib/ecore_x/xlib/ecore_x_vsync.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #define ECORE_X_VSYNC_DRM 1 @@ -85,22 +86,30 @@ static int (*sym_drmWaitVBlank)(int fd, static int (*sym_drmHandleEvent)(int fd, drmEventContext *evctx) = NULL; static int drm_fd = -1; -static int drm_event_is_busy = 0; +static volatile int drm_event_is_busy = 0; static int drm_animators_interval = 1; static drmEventContext drm_evctx; -static Ecore_Fd_Handler *dri_drm_fdh = NULL; -static Ecore_Timer *_drm_fail_timer = NULL; static double _drm_fail_time = 0.1; static double _drm_fail_time2 = 1.0 / 60.0; static int _drm_fail_count = 0; static void *drm_lib = NULL; +static Eina_Thread_Queue *thq = NULL; +static Ecore_Thread *drm_thread = NULL; +typedef struct +{ + Eina_Thread_Queue_Msg head; + char val; +} Msg; + + static Eina_Bool _drm_tick_schedule(void) { drmVBlank vbl; + DBG("sched...\n"); vbl.request.type = DRM_VBLANK_RELATIVE | DRM_VBLANK_EVENT; vbl.request.sequence = drm_animators_interval; vbl.request.signal = 0; @@ -108,40 +117,43 @@ _drm_tick_schedule(void) return EINA_TRUE; } +static void +_tick_send(char val) +{ + Msg *msg; + void *ref; + DBG("_tick_send(%i)\n", val); + msg = eina_thread_queue_send(thq, sizeof(Msg), &ref); + msg->val = val; + eina_thread_queue_send_done(thq, ref); +} + static void _drm_tick_begin(void *data EINA_UNUSED) { + _drm_fail_count = 0; drm_event_is_busy = 1; - _drm_tick_schedule(); + _tick_send(1); } static void _drm_tick_end(void *data EINA_UNUSED) { - if (_drm_fail_timer) - { - ecore_timer_del(_drm_fail_timer); - _drm_fail_timer = NULL; - } + _drm_fail_count = 0; drm_event_is_busy = 0; + _tick_send(0); } -static Eina_Bool -_drm_fail_cb(void *data EINA_UNUSED) +static void +_drm_send_time(double t) { - if (drm_event_is_busy) + double *tim = malloc(sizeof(*tim)); + if (tim) { - _drm_fail_count++; - DBG("VSYNC FAIL %i %3.8f", _drm_fail_count, ecore_loop_time_get()); - ecore_animator_custom_tick(); - _drm_tick_schedule(); - if (_drm_fail_count == 10) - { - _drm_fail_timer = ecore_timer_add(_drm_fail_time2, _drm_fail_cb, NULL); - return EINA_FALSE; - } + *tim = t; + DBG(" ... send %1.8f\n", t); + ecore_thread_feedback(drm_thread, tim); } - return EINA_TRUE; } static void @@ -153,35 +165,108 @@ _drm_vblank_handler(int fd EINA_UNUSED, { if (drm_event_is_busy) { - double tim; - static double ptim = 0.0; static unsigned int pframe = 0; + DBG("vblank %i\n", frame); if (pframe != frame) { - tim = (double)sec + ((double)usec / 1000000); - DBG("VSYNC %1.8f [%i] = delt %1.8f\n", tim, frame - pframe, tim - ptim); - ecore_loop_time_set(tim); - ecore_animator_custom_tick(); + _drm_send_time((double)sec + ((double)usec / 1000000)); _drm_tick_schedule(); pframe = frame; - ptim = tim; - if (_drm_fail_timer) ecore_timer_del(_drm_fail_timer); - _drm_fail_timer = ecore_timer_add(_drm_fail_time, _drm_fail_cb, NULL); - _drm_fail_count = 0; } } } -static Eina_Bool -_drm_cb(void *data EINA_UNUSED, - Ecore_Fd_Handler *fd_handler EINA_UNUSED) +static void +_drm_tick_core(void *data EINA_UNUSED, Ecore_Thread *thread EINA_UNUSED) { - // XXX: move this to a thread whose only job it is to select on the drm - // fd and get the first vsync and send it back to the mainloop to wakeup - // and ignore the others - sym_drmHandleEvent(drm_fd, &drm_evctx); - return ECORE_CALLBACK_RENEW; + Msg *msg; + void *ref; + int tick = 0; + + for (;;) + { + DBG("------- drm_event_is_busy=%i\n", drm_event_is_busy); + if (!drm_event_is_busy) + { + DBG("wait...\n"); + msg = eina_thread_queue_wait(thq, &ref); + if (msg) + { + tick = msg->val; + eina_thread_queue_wait_done(thq, ref); + } + } + else + { +again: + DBG("poll...\n"); + msg = eina_thread_queue_poll(thq, &ref); + if (msg) + { + tick = msg->val; + eina_thread_queue_wait_done(thq, ref); + } + if (msg) goto again; + } + DBG("tick = %i\n", tick); + if (tick == -1) + { + drm_thread = NULL; + eina_thread_queue_free(thq); + thq = NULL; + return; + } + else if (tick) + { + fd_set rfds, wfds, exfds; + int max_fd; + int ret; + struct timeval tv; + + _drm_tick_schedule(); + max_fd = 0; + FD_ZERO(&rfds); + FD_ZERO(&wfds); + FD_ZERO(&exfds); + FD_SET(drm_fd, &rfds); + max_fd = drm_fd; + tv.tv_sec = 0; + if (_drm_fail_count >= 10) + tv.tv_usec = _drm_fail_time2 * 1000000; + else + tv.tv_usec = _drm_fail_time * 1000000; + ret = select(max_fd + 1, &rfds, &wfds, &exfds, &tv); + if ((ret == 1) && (FD_ISSET(drm_fd, &rfds))) + { + sym_drmHandleEvent(drm_fd, &drm_evctx); + _drm_fail_count = 0; + } + else if (ret == 0) + { + // timeout + _drm_send_time(ecore_time_get()); + _drm_fail_count++; + } + } + } +} + +static void +_drm_tick_notify(void *data EINA_UNUSED, Ecore_Thread *thread EINA_UNUSED, void *msg) +{ + DBG("notify.... %3.3f %i\n", *((double *)msg), drm_event_is_busy); + if (drm_event_is_busy) + { + double *t = msg; + static double pt = 0.0; + + DBG("VSYNC %1.8f = delt %1.8f\n", *t, *t - pt); + ecore_loop_time_set(*t); + ecore_animator_custom_tick(); + pt = *t; + } + free(msg); } // yes. most evil. we dlopen libdrm and libGL etc. to manually find smbols @@ -258,54 +343,17 @@ _drm_init(void) return 0; } - dri_drm_fdh = ecore_main_fd_handler_add(drm_fd, ECORE_FD_READ, - _drm_cb, NULL, NULL, NULL); - if (!dri_drm_fdh) - { - close(drm_fd); - drm_fd = -1; - return 0; - } + thq = eina_thread_queue_new(); + drm_thread = ecore_thread_feedback_run(_drm_tick_core, _drm_tick_notify, + NULL, NULL, NULL, EINA_TRUE); return 1; } -static void -_drm_shutdown(void) -{ - if (drm_fd >= 0) - { - close(drm_fd); - drm_fd = -1; - } - if (dri_drm_fdh) - { - ecore_main_fd_handler_del(dri_drm_fdh); - dri_drm_fdh = NULL; - } - if (_drm_fail_timer) - { - ecore_timer_del(_drm_fail_timer); - _drm_fail_timer = NULL; - } -} - static Eina_Bool _drm_animator_tick_source_set(void) { if (vsync_root) { - if (!_drm_link()) - { - ecore_animator_source_set(ECORE_ANIMATOR_SOURCE_TIMER); - return EINA_FALSE; - } - _drm_shutdown(); - if (!_drm_init()) - { - vsync_root = 0; - ecore_animator_source_set(ECORE_ANIMATOR_SOURCE_TIMER); - return EINA_FALSE; - } ecore_animator_custom_source_tick_begin_callback_set (_drm_tick_begin, NULL); ecore_animator_custom_source_tick_end_callback_set @@ -316,7 +364,7 @@ _drm_animator_tick_source_set(void) { if (drm_fd >= 0) { - _drm_shutdown(); + _drm_tick_end(NULL); ecore_animator_custom_source_tick_begin_callback_set (NULL, NULL); ecore_animator_custom_source_tick_end_callback_set @@ -482,7 +530,6 @@ _vsync_init(void) { if (_drm_link()) { - _drm_shutdown(); if (_drm_init()) { mode = 1; @@ -507,8 +554,6 @@ ecore_x_vsync_animator_tick_source_set(Ecore_X_Window win) { Ecore_X_Window root; -// XXX: disable vsync for now until i fix this - return EINA_FALSE; root = ecore_x_window_root_get(win); if (root != vsync_root) {