3 #endif /* ifdef HAVE_CONFIG_H */
6 #include "ecore_x_private.h"
15 #include <sys/types.h>
17 #include <sys/select.h>
21 # include <sys/prctl.h>
24 #define ECORE_X_VSYNC_DRM 1
26 static Ecore_X_Window vsync_root = 0;
28 int _ecore_x_image_shm_check(void);
30 static int _vsync_log_dom = -1;
33 #define ERR(...) EINA_LOG_DOM_ERR(_vsync_log_dom, __VA_ARGS__)
36 #define DBG(...) EINA_LOG_DOM_DBG(_vsync_log_dom, __VA_ARGS__)
39 #define INF(...) EINA_LOG_DOM_INFO(_vsync_log_dom, __VA_ARGS__)
42 #define WRN(...) EINA_LOG_DOM_WARN(_vsync_log_dom, __VA_ARGS__)
45 #define CRI(...) EINA_LOG_DOM_CRIT(_vsync_log_dom, __VA_ARGS__)
49 #ifdef ECORE_X_VSYNC_DRM
50 // relevant header bits of dri/drm inlined here to avoid needing external
55 DRM_VBLANK_ABSOLUTE = 0x00000000,
56 DRM_VBLANK_RELATIVE = 0x00000001,
57 DRM_VBLANK_EVENT = 0x04000000,
58 DRM_VBLANK_FLIP = 0x08000000,
59 DRM_VBLANK_NEXTONMISS = 0x10000000,
60 DRM_VBLANK_SECONDARY = 0x20000000,
61 DRM_VBLANK_SIGNAL = 0x40000000
65 typedef struct _drmVBlankReq
67 drmVBlankSeqType type;
68 unsigned int sequence;
72 typedef struct _drmVBlankReply
74 drmVBlankSeqType type;
75 unsigned int sequence;
80 typedef union _drmVBlank
86 #define DRM_EVENT_CONTEXT_VERSION 2
88 typedef struct _drmEventContext
91 void (*vblank_handler)(int fd,
92 unsigned int sequence,
96 void (*page_flip_handler)(int fd,
97 unsigned int sequence,
103 typedef struct _drmVersionBroken
107 // int version_patchlevel;
109 // WARNING! this does NOT match the system drm.h headers because
110 // literally drm.h is wrong. the below is correct. drm hapily
111 // broke its ABI at some point.
119 typedef struct _drmVersion
123 int version_patchlevel;
125 // WARNING! this does NOT match the system drm.h headers because
126 // literally drm.h is wrong. the below is correct. drm hapily
127 // broke its ABI at some point.
135 static int (*sym_drmClose)(int fd) = NULL;
136 static int (*sym_drmWaitVBlank)(int fd,
137 drmVBlank *vbl) = NULL;
138 static int (*sym_drmHandleEvent)(int fd,
139 drmEventContext *evctx) = NULL;
140 static void *(*sym_drmGetVersion)(int fd) = NULL;
141 static void (*sym_drmFreeVersion)(void *drmver) = NULL;
142 static int drm_fd = -1;
143 static volatile int drm_event_is_busy = 0;
144 static int drm_animators_interval = 1;
145 static drmEventContext drm_evctx;
146 static double _drm_fail_time = 0.1;
147 static double _drm_fail_time2 = 1.0 / 60.0;
148 static int _drm_fail_count = 0;
150 static void *drm_lib = NULL;
152 static Eina_Thread_Queue *thq = NULL;
153 static Ecore_Thread *drm_thread = NULL;
154 static Eina_Spinlock tick_queue_lock;
155 static int tick_queue_count = 0;
156 static Eina_Bool tick_skip = EINA_FALSE;
160 Eina_Thread_Queue_Msg head;
165 # define D(args...) fprintf(stderr, ##args)
171 _drm_tick_schedule(void)
176 vbl.request.type = DRM_VBLANK_RELATIVE | DRM_VBLANK_EVENT;
177 vbl.request.sequence = drm_animators_interval;
178 vbl.request.signal = 0;
179 if (sym_drmWaitVBlank(drm_fd, &vbl) < 0) return EINA_FALSE;
188 DBG("_tick_send(%i)", val);
189 msg = eina_thread_queue_send(thq, sizeof(Msg), &ref);
191 eina_thread_queue_send_done(thq, ref);
195 _drm_tick_begin(void *data EINA_UNUSED)
198 drm_event_is_busy = 1;
203 _drm_tick_end(void *data EINA_UNUSED)
206 drm_event_is_busy = 0;
211 _drm_send_time(double t)
213 double *tim = malloc(sizeof(*tim));
217 DBG(" ... send %1.8f", t);
218 D(" @%1.5f ... send %1.8f\n", ecore_time_get(), t);
219 eina_spinlock_take(&tick_queue_lock);
221 eina_spinlock_release(&tick_queue_lock);
222 ecore_thread_feedback(drm_thread, tim);
227 _drm_vblank_handler(int fd EINA_UNUSED,
231 void *data EINA_UNUSED)
233 if (drm_event_is_busy)
235 static unsigned int pframe = 0;
237 DBG("vblank %i", frame);
238 D(" @%1.5f vblank %i\n", ecore_time_get(), frame);
241 #define DELTA_COUNT 10
242 double t = (double)sec + ((double)usec / 1000000);
243 double tnow = ecore_time_get();
244 static double tdelta[DELTA_COUNT];
245 static double tdelta_avg = 0.0;
246 static int tdelta_n = 0;
250 if (tdelta_n > DELTA_COUNT)
254 else if (tdelta_n < DELTA_COUNT)
256 tdelta[tdelta_n] = tnow - t;
260 else if (tdelta_n == DELTA_COUNT)
264 for (i = 0; i < DELTA_COUNT; i++)
265 tdelta_avg += tdelta[i];
266 tdelta_avg /= (double)(DELTA_COUNT);
281 D(" @%1.5f vblank drm event when not busy!\n", ecore_time_get());
286 _drm_tick_core(void *data EINA_UNUSED, Ecore_Thread *thread)
292 eina_thread_name_set(eina_thread_self(), "Eanimator-vsync");
294 prctl(PR_SET_TIMERSLACK, 1, 0, 0, 0);
296 while (!ecore_thread_check(thread))
298 DBG("------- drm_event_is_busy=%i", drm_event_is_busy);
299 D(" @%1.5f ------- drm_event_is_busy=%i\n", ecore_time_get(), drm_event_is_busy);
300 if (!drm_event_is_busy)
303 D(" @1.5f wait...\n", ecore_time_get());
304 msg = eina_thread_queue_wait(thq, &ref);
308 eina_thread_queue_wait_done(thq, ref);
316 D(" @%1.5f poll...\n", ecore_time_get());
317 msg = eina_thread_queue_poll(thq, &ref);
321 eina_thread_queue_wait_done(thq, ref);
326 DBG("tick = %i", tick);
327 D(" @%1.5f tick = %i\n", ecore_time_get(), tick);
331 eina_thread_queue_free(thq);
337 fd_set rfds, wfds, exfds;
342 if (!_drm_tick_schedule())
344 D(" @%1.5f schedule fail\n", ecore_time_get());
345 _drm_fail_count = 999999;
351 FD_SET(drm_fd, &rfds);
354 if (_drm_fail_count >= 10)
355 tv.tv_usec = _drm_fail_time2 * 1000000;
357 tv.tv_usec = _drm_fail_time * 1000000;
358 D(" @%1.5f wait %ims\n", ecore_time_get(), (int)(tv.tv_usec /1000));
359 ret = select(max_fd + 1, &rfds, &wfds, &exfds, &tv);
360 if ((ret == 1) && (FD_ISSET(drm_fd, &rfds)))
362 D(" @%1.5f have event\n", ecore_time_get());
363 sym_drmHandleEvent(drm_fd, &drm_evctx);
369 _drm_send_time(ecore_time_get());
371 D(" @%1.5f fail count %i\n", ecore_time_get(), _drm_fail_count);
378 _drm_tick_notify(void *data EINA_UNUSED, Ecore_Thread *thread EINA_UNUSED, void *msg)
382 eina_spinlock_take(&tick_queue_lock);
383 tick_queued = tick_queue_count;
385 eina_spinlock_release(&tick_queue_lock);
386 DBG("notify.... %3.3f %i", *((double *)msg), drm_event_is_busy);
387 D("notify.... %3.3f %i\n", *((double *)msg), drm_event_is_busy);
388 if (drm_event_is_busy)
391 static double pt = 0.0;
393 DBG("VSYNC %1.8f = delt %1.8f", *t, *t - pt);
394 D("VSYNC %1.8f = delt %1.8f\n", *t, *t - pt);
395 if ((!tick_skip) || (tick_queued == 1))
397 ecore_loop_time_set(*t);
398 ecore_animator_custom_tick();
405 // yes. most evil. we dlopen libdrm and libGL etc. to manually find smbols
406 // so we can be as compatible as possible given the whole mess of the
407 // gl/dri/drm etc. world. and handle graceful failure at runtime not
412 const char *drm_libs[] =
421 #define SYM(lib, xx) \
423 sym_ ## xx = dlsym(lib, #xx); \
424 if (!(sym_ ## xx)) { \
429 if (drm_lib) return 1;
430 for (i = 0; drm_libs[i]; i++)
432 drm_lib = dlopen(drm_libs[i], RTLD_LOCAL | RTLD_LAZY);
436 SYM(drm_lib, drmClose);
437 SYM(drm_lib, drmWaitVBlank);
438 SYM(drm_lib, drmHandleEvent);
439 SYM(drm_lib, drmGetVersion);
440 SYM(drm_lib, drmFreeVersion);
449 if (!drm_lib) return 0;
453 #define DRM_HAVE_NVIDIA 1
456 _drm_init(int *flags)
460 Eina_Bool ok = EINA_FALSE;
461 int vmaj = 0, vmin = 0;
463 // vboxvideo 4.3.14 is crashing when calls drmWaitVBlank()
464 // https://www.virtualbox.org/ticket/13265
465 // also affects 4.3.12
466 if (stat("/sys/module/vboxvideo", &st) == 0)
469 FILE *fp = fopen("/sys/module/vboxvideo/version", "rb");
472 if (fgets(buf, sizeof(buf), fp))
474 if (eina_str_has_prefix(buf, "4.3.14"))
485 // only do this on new kernels = let's say 3.14 and up. 3.16 definitely
488 FILE *fp = fopen("/proc/sys/kernel/osrelease", "rb");
491 if (fgets(buf, sizeof(buf), fp))
493 if (sscanf(buf, "%i.%i.%*s", &vmaj, &vmin) == 2)
495 if (vmaj >= 3) ok = EINA_TRUE;
504 snprintf(buf, sizeof(buf), "/dev/dri/card1");
505 if (stat(buf, &st) == 0)
507 // XXX: 2 dri cards - ambiguous. unknown device for screen
510 snprintf(buf, sizeof(buf), "/dev/dri/card0");
511 if (stat(buf, &st) != 0) return 0;
512 drm_fd = open(buf, O_RDWR | O_CLOEXEC);
513 if (drm_fd < 0) return 0;
515 if (!getenv("ECORE_VSYNC_DRM_ALL"))
518 drmVersionBroken *drmverbroken;
520 drmver = sym_drmGetVersion(drm_fd);
521 drmverbroken = (drmVersionBroken *)drmver;
527 // sanity check the drm version structure due to public versions
528 // not matching the real memory layout, check drm version
529 // is recent (1.6+) and name and sec ptrs exist AND their lengths are
530 // not garbage (within a sensible range)
531 if (getenv("ECORE_VSYNC_DRM_VERSION_DEBUG"))
533 if ((drmverbroken->name > (char *)4000L) &&
534 (drmverbroken->date_len < 200))
536 "!BROKEN DRM! Do FIXUP of ABI\n"
537 "DRM Version: %i.%i\n"
541 drmverbroken->version_major, drmverbroken->version_minor,
542 drmverbroken->name, drmverbroken->date, drmverbroken->desc);
546 "DRM Version: %i.%i\n"
550 drmver->version_major, drmver->version_minor,
551 drmver->name, drmver->date, drmver->desc);
553 if ((((drmver->version_major == 1) &&
554 (drmver->version_minor >= 6)) ||
555 (drmver->version_major > 1)) &&
556 (drmver->name > (char *)4000L) &&
557 (drmver->date_len < 200))
559 // whitelist of known-to-work drivers
560 if ((!strcmp(drmver->name, "exynos")) &&
561 (strstr(drmver->desc, "Samsung")))
563 if (((vmaj >= 3) && (vmin >= 0)) || (vmaj >= 4))
565 if (getenv("ECORE_VSYNC_DRM_VERSION_DEBUG"))
566 fprintf(stderr, "Whitelisted exynos OK\n");
571 if ((!strcmp(drmver->name, "i915")) &&
572 (strstr(drmver->desc, "Intel Graphics")))
574 if (((vmaj >= 3) && (vmin >= 14)) || (vmaj >= 4))
576 if (getenv("ECORE_VSYNC_DRM_VERSION_DEBUG"))
577 fprintf(stderr, "Whitelisted intel OK\n");
582 if ((!strcmp(drmver->name, "radeon")) &&
583 (strstr(drmver->desc, "Radeon")) &&
584 (((drmver->version_major == 2) &&
585 (drmver->version_minor >= 39)) ||
586 (drmver->version_major > 2)))
588 if (((vmaj >= 3) && (vmin >= 14)) || (vmaj >= 4))
590 if (getenv("ECORE_VSYNC_DRM_VERSION_DEBUG"))
591 fprintf(stderr, "Whitelisted radeon OK\n");
597 if ((((drmverbroken->version_major == 1) &&
598 (drmverbroken->version_minor >= 6)) ||
599 (drmverbroken->version_major > 1)) &&
600 (drmverbroken->name > (char *)4000L) &&
601 (drmverbroken->date_len < 200))
603 // whitelist of known-to-work drivers
604 if ((!strcmp(drmverbroken->name, "exynos")) &&
605 (strstr(drmverbroken->desc, "Samsung")))
607 if (((vmaj >= 3) && (vmin >= 0)) || (vmaj >= 4))
609 if (getenv("ECORE_VSYNC_DRM_VERSION_DEBUG"))
610 fprintf(stderr, "Whitelisted exynos OK\n");
615 if ((!strcmp(drmverbroken->name, "i915")) &&
616 (strstr(drmverbroken->desc, "Intel Graphics")))
618 if (((vmaj >= 3) && (vmin >= 14)) || (vmaj >= 4))
620 if (getenv("ECORE_VSYNC_DRM_VERSION_DEBUG"))
621 fprintf(stderr, "Whitelisted intel OK\n");
626 if ((!strcmp(drmverbroken->name, "radeon")) &&
627 (strstr(drmverbroken->desc, "Radeon")) &&
628 (((drmver->version_major == 2) &&
629 (drmver->version_minor >= 39)) ||
630 (drmver->version_major > 2)))
632 if (((vmaj >= 3) && (vmin >= 14)) || (vmaj >= 4))
634 if (getenv("ECORE_VSYNC_DRM_VERSION_DEBUG"))
635 fprintf(stderr, "Whitelisted radeon OK\n");
641 if ((drmver->version_major >= 0) &&
642 (drmver->version_minor >= 0) &&
643 (drmver->name > (char *)4000L) &&
644 (drmver->date_len < 200))
646 if ((!strcmp(drmver->name, "nvidia-drm")) &&
647 (strstr(drmver->desc, "NVIDIA DRM driver")))
649 if (((vmaj >= 3) && (vmin >= 14)) || (vmaj >= 4))
651 *flags |= DRM_HAVE_NVIDIA;
656 if ((drmverbroken->version_major >= 0) &&
657 (drmverbroken->version_minor >= 0) &&
658 (drmverbroken->name > (char *)4000L) &&
659 (drmverbroken->date_len < 200))
661 if ((!strcmp(drmverbroken->name, "nvidia-drm")) &&
662 (strstr(drmverbroken->desc, "NVIDIA DRM driver")))
664 if (((vmaj >= 3) && (vmin >= 14)) || (vmaj >= 4))
666 *flags |= DRM_HAVE_NVIDIA;
672 sym_drmFreeVersion(drmver);
680 memset(&drm_evctx, 0, sizeof(drm_evctx));
681 drm_evctx.version = DRM_EVENT_CONTEXT_VERSION;
682 drm_evctx.vblank_handler = _drm_vblank_handler;
683 drm_evctx.page_flip_handler = NULL;
685 if (!_drm_tick_schedule())
692 if (getenv("ECORE_ANIMATOR_SKIP")) tick_skip = EINA_TRUE;
693 tick_queue_count = 0;
694 eina_spinlock_new(&tick_queue_lock);
695 thq = eina_thread_queue_new();
696 drm_thread = ecore_thread_feedback_run(_drm_tick_core, _drm_tick_notify,
697 NULL, NULL, NULL, EINA_TRUE);
702 _drm_animator_tick_source_set(void)
706 ecore_animator_custom_source_tick_begin_callback_set
707 (_drm_tick_begin, NULL);
708 ecore_animator_custom_source_tick_end_callback_set
709 (_drm_tick_end, NULL);
710 ecore_animator_source_set(ECORE_ANIMATOR_SOURCE_CUSTOM);
717 ecore_animator_custom_source_tick_begin_callback_set
719 ecore_animator_custom_source_tick_end_callback_set
721 ecore_animator_source_set(ECORE_ANIMATOR_SOURCE_TIMER);
734 // XXX: missing mode 3 == separate x connection with compiled in dri2 proto
735 // handling ala mesa (taken from mesa likely)
748 _vsync_log_dom = eina_log_domain_register("ecore_x_vsync", EINA_COLOR_LIGHTRED);
749 if (_ecore_x_image_shm_check())
751 #ifdef ECORE_X_VSYNC_DRM
752 // preferred inline drm if possible
753 if (!stat("/dev/dri/card0", &stb))
757 if (_drm_init(&flags)) mode = 1;
766 ecore_x_vsync_animator_tick_source_set(Ecore_X_Window win)
769 static int vsync_veto = -1;
771 if (vsync_veto == -1)
777 home = eina_environment_home_get();
778 if (!home) eina_environment_tmp_get();
779 snprintf(buf, sizeof(buf), "%s/.ecore-no-vsync", home);
780 if (getenv("ECORE_NO_VSYNC")) vsync_veto = 1;
781 else if (stat(buf, &st) == 0) vsync_veto = 1;
782 else if (stat("/etc/.ecore-no-vsync", &st) == 0) vsync_veto = 1;
785 if (vsync_veto == 1) return EINA_FALSE;
787 root = ecore_x_window_root_get(win);
788 if (root != vsync_root)
792 #ifdef ECORE_X_VSYNC_DRM
793 if (mode == 1) return _drm_animator_tick_source_set();