ecore_wayland: To use vsync as custom source of animator 78/123078/2
authorjiin.moon <jiin.moon@samsung.com>
Fri, 13 Jan 2017 04:05:54 +0000 (13:05 +0900)
committerGerrit Code Review <gerrit@review.vlan103.tizen.org>
Wed, 5 Apr 2017 08:22:44 +0000 (01:22 -0700)
If user does not call "ecore_animator_source_set(ECORE_ANIMATOR_SOURCE_CUSTOM)",
animator would be working by timer.
it means this patch does not effect without setting by user.

I will check more regarding resource leak

Change-Id: I679dc43c6657d80ac5bdc424945c44fd0c050e6a

configure.ac
packaging/efl.spec
src/Makefile_Ecore_Wayland.am
src/lib/ecore/Ecore_Common.h
src/lib/ecore/ecore_anim.c
src/lib/ecore_wayland/ecore_wl.c
src/lib/ecore_wayland/ecore_wl_anim_vsync.c [new file with mode: 0644]
src/lib/ecore_wayland/ecore_wl_private.h

index 7621fdb..2bf07d2 100755 (executable)
@@ -3139,7 +3139,7 @@ EFL_INTERNAL_DEPEND_PKG([ECORE_WAYLAND], [eo])
 EFL_INTERNAL_DEPEND_PKG([ECORE_WAYLAND], [eina])
 
 EFL_DEPEND_PKG([ECORE_WAYLAND], [WAYLAND],
-   [wayland-client >= 1.3.0 wayland-cursor >= 1.3.0 xkbcommon >= 0.3.0 xdg-shell-client text-client tizen-extension-client wayland-tbm-client])
+   [wayland-client >= 1.3.0 wayland-cursor >= 1.3.0 xkbcommon >= 0.3.0 xdg-shell-client text-client tizen-extension-client wayland-tbm-client libtdm-client])
 
 EFL_EVAL_PKGS([ECORE_WAYLAND])
 
index 3b2072a..7f93e57 100644 (file)
@@ -106,6 +106,7 @@ BuildRequires:  pkgconfig(xcb-dri3)
 BuildRequires:  pkgconfig(gbm)
 BuildRequires:  pkgconfig(libtbm)
 BuildRequires:  pkgconfig(libtdm)
+BuildRequires:  pkgconfig(libtdm-client)
 BuildRequires:  pkgconfig(libdrm)
 BuildRequires:  pkgconfig(wayland-client)
 BuildRequires:  pkgconfig(wayland-server)
index 614c698..c68eaed 100644 (file)
@@ -15,6 +15,7 @@ lib/ecore_wayland/ecore_wl_input.c \
 lib/ecore_wayland/ecore_wl_output.c \
 lib/ecore_wayland/ecore_wl_window.c \
 lib/ecore_wayland/ecore_wl_subsurf.c \
+lib/ecore_wayland/ecore_wl_anim_vsync.c \
 lib/ecore_wayland/ecore_wl_private.h \
 lib/ecore_wayland/ivi-application-protocol.c \
 lib/ecore_wayland/ivi-application-client-protocol.h \
index 2e4e012..0d6556b 100644 (file)
@@ -3470,6 +3470,23 @@ EAPI void ecore_animator_custom_source_tick_begin_callback_set(Ecore_Cb func, co
  */
 EAPI void ecore_animator_custom_source_tick_end_callback_set(Ecore_Cb func, const void *data);
 
+
+/* TIZEN_ONLY : To use vsync as a custom source of animator */
+/**
+ * @internal
+ * @brief Set the function that quit a custom animator tick source
+ *
+ * @param func The function to call when ticking is to quit
+ * @param data The data passed to the tick quit function as its parameter
+ *
+ * @warning Do not use this function unless you know what you are doing.
+ *
+ */
+
+EAPI void ecore_animator_custom_source_tick_quit_callback_set(Ecore_Cb func, const void *data);
+/* TIZEN_ONLY : To use vsync as a custom source of animator */
+
+
 /**
  * @brief Triggers a custom animator tick.
  *
index a1d1c46..78dd14d 100644 (file)
@@ -76,6 +76,12 @@ static Ecore_Cb begin_tick_cb = NULL;
 static const void *begin_tick_data = NULL;
 static Ecore_Cb end_tick_cb = NULL;
 static const void *end_tick_data = NULL;
+
+/* TIZEN_ONLY : To use vsync as a custom source of animator */
+static Ecore_Cb quit_tick_cb = NULL;
+static const void *quit_tick_data = NULL;
+/* TIZEN_ONLY : To use vsync as a custom source of animator */
+
 static Eina_Bool animator_ran = EINA_FALSE;
 
 static int timer_fd_read = -1;
@@ -848,10 +854,29 @@ ecore_animator_custom_tick(void)
    _ecore_unlock();
 }
 
+/* TIZEN_ONLY : To use vsync as a custom source of animator */
+EAPI void
+ecore_animator_custom_source_tick_quit_callback_set(Ecore_Cb func,
+                                                     EINA_UNUSED const void *data)
+{
+   EINA_MAIN_LOOP_CHECK_RETURN;
+   _ecore_lock();
+   _end_tick();
+   quit_tick_cb = func;
+   quit_tick_data = data;
+   if (_have_animators()) _begin_tick();
+   _ecore_unlock();
+}
+/* TIZEN_ONLY : To use vsync as a custom source of animator */
+
 void
 _ecore_animator_shutdown(void)
 {
    _timer_tick_quit();
+
+/* TIZEN_ONLY : To use vsync as a custom source of animator */
+   if (src == ECORE_ANIMATOR_SOURCE_CUSTOM && quit_tick_cb) quit_tick_cb((void *)quit_tick_data);
+
    _end_tick();
    while (animators)
      {
index 537395e..ab9efcc 100644 (file)
@@ -610,16 +610,16 @@ ecore_wl_animator_source_set(Ecore_Animator_Source source)
 
    if (_ecore_wl_server_mode) return EINA_FALSE;
 
-   /* FIXME: check existing source. If custom, disable anim_callbacks */
-
-   /* based on the animator source we are using, setup or destroy callbacks */
+   /* TIZEN_ONLY : To use vsync as custom source of animator */
    switch (source)
      {
       case ECORE_ANIMATOR_SOURCE_CUSTOM:
         ecore_animator_custom_source_tick_begin_callback_set
-          (_ecore_wl_animator_tick_cb_begin, NULL);
+          (_ecore_wl_animator_vsync_tick_begin, NULL);
         ecore_animator_custom_source_tick_end_callback_set
-          (_ecore_wl_animator_tick_cb_end, NULL);
+          (_ecore_wl_animator_vsync_tick_end, NULL);
+        ecore_animator_custom_source_tick_quit_callback_set
+          (_ecore_wl_animator_vsync_tick_quit, NULL);
         break;
       case ECORE_ANIMATOR_SOURCE_TIMER:
         ecore_animator_custom_source_tick_begin_callback_set(NULL, NULL);
diff --git a/src/lib/ecore_wayland/ecore_wl_anim_vsync.c b/src/lib/ecore_wayland/ecore_wl_anim_vsync.c
new file mode 100644 (file)
index 0000000..7a964ce
--- /dev/null
@@ -0,0 +1,398 @@
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* ifdef HAVE_CONFIG_H */
+
+#include "Ecore.h"
+
+#include <string.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <dlfcn.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <tdm_client.h>
+#include <poll.h>
+#include <errno.h>
+
+static tdm_client *client = NULL;
+static tdm_client_output *output = NULL;
+static tdm_client_vblank *vblank = NULL;
+static Eina_Bool vblank_wait= 0;
+static int _vsync_log_dom = -1;
+static int tdm_fd = -1;
+static unsigned int _tdm_req_fps = (1.0 / 60.0) * 1000;
+static Eina_Thread_Queue *thq = NULL;
+static Ecore_Thread *tdm_thread = NULL;
+static Eina_Bool tdm_event_is_busy = EINA_FALSE;
+static Eina_Bool tick_skip = EINA_FALSE;
+static Eina_Spinlock tick_queue_lock;
+static int           tick_queue_count = 0;
+typedef struct
+{
+   Eina_Thread_Queue_Msg head;
+   char val;
+} Msg;
+
+#define DELTA_COUNT 10
+#undef ERR
+#define ERR(...) EINA_LOG_DOM_ERR(_vsync_log_dom, __VA_ARGS__)
+
+#undef DBG
+#define DBG(...) EINA_LOG_DOM_DBG(_vsync_log_dom, __VA_ARGS__)
+
+#undef INF
+#define INF(...) EINA_LOG_DOM_INFO(_vsync_log_dom, __VA_ARGS__)
+
+#undef WRN
+#define WRN(...) EINA_LOG_DOM_WARN(_vsync_log_dom, __VA_ARGS__)
+
+#undef CRI
+#define CRI(...) EINA_LOG_DOM_CRIT(_vsync_log_dom, __VA_ARGS__)
+
+
+static void
+_tick_send(char val)
+{
+   Msg *msg;
+   void *ref;
+   msg = eina_thread_queue_send(thq, sizeof(Msg), &ref);
+   msg->val = val;
+   eina_thread_queue_send_done(thq, ref);
+}
+
+static void
+_tdm_send_time(double t)
+{
+   double *tim = malloc(sizeof(*tim));
+   if (tim)
+     {
+        *tim = t;
+        DBG("tdm send time  @%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(tdm_thread, tim);
+     }
+}
+
+static void
+_tdm_vblank_handler(tdm_client_vblank *vblank EINA_UNUSED, tdm_error error EINA_UNUSED, unsigned int sequence,
+                    unsigned int tv_sec, unsigned int tv_usec, void *user_data EINA_UNUSED)
+{
+   vblank_wait = 0;
+
+   if (tdm_event_is_busy)
+     {
+        static unsigned int pseq = 0;
+        if (pseq != sequence)
+          {
+             double t = (double)tv_sec + ((double)tv_usec / 1000000);
+             double tnow = ecore_time_get();
+             static double tdelta[DELTA_COUNT];
+             static double tdelta_avg = 0.0;
+             static int tdelta_n = 0;
+
+             if (t > tnow)
+               {
+                  if (tdelta_n > DELTA_COUNT)
+                    {
+                       t = t + tdelta_avg;
+                    }
+                  else if (tdelta_n < DELTA_COUNT)
+                    {
+                       tdelta[tdelta_n] = tnow - t;
+                       tdelta_n++;
+                       t = tnow;
+                    }
+                  else if (tdelta_n == DELTA_COUNT)
+                    {
+                       int i;
+                       for (i = 0; i < DELTA_COUNT; i++)
+                         tdelta_avg += tdelta[i];
+                       tdelta_avg /= (double)(DELTA_COUNT);
+                       tdelta_n++;
+                    }
+               }
+             else
+               {
+                  tdelta_avg = 0.0;
+                  tdelta_n = 0;
+               }
+             _tdm_send_time(t);
+             pseq = sequence;
+          }
+     }
+}
+
+static tdm_error
+_tdm_tick_schedule(void)
+{
+   tdm_error err;
+   err = tdm_client_vblank_wait(vblank, 1, _tdm_vblank_handler, NULL);
+   if (err != TDM_ERROR_NONE)
+     ERR("tdm client vblank wait failed %d\n", err);
+   else vblank_wait = 1;
+   return err;
+}
+
+static void
+_tdm_tick_core(void *data EINA_UNUSED, Ecore_Thread *thread)
+{
+   Msg *msg;
+   void *ref;
+   int tick = 0, fail_cnt = 0;
+   tdm_error err;
+   eina_thread_name_set(eina_thread_self(), "Eanimator-vsync");
+   struct pollfd fds;
+
+   fds.events = POLLIN;
+   fds.fd = tdm_fd;
+   fds.revents = 0;
+   while (!ecore_thread_check(thread))
+     {
+        DBG("------- tdm_event_is_busy=%i", tdm_event_is_busy);
+        if (!tdm_event_is_busy)
+          {
+             DBG("wait...");
+             msg = eina_thread_queue_wait(thq, &ref);
+             if (msg)
+               {
+                  tick = msg->val;
+                  eina_thread_queue_wait_done(thq, ref);
+               }
+          }
+        else
+          {
+             do
+               {
+                  DBG("poll...");
+                  msg = eina_thread_queue_poll(thq, &ref);
+                  if (msg)
+                    {
+                       tick = msg->val;
+                       eina_thread_queue_wait_done(thq, ref);
+                    }
+               }
+             while (msg);
+          }
+        if (tick == -1)
+             goto done;
+        else if ((tick == 1) && (!vblank_wait))
+          {
+             fd_set rfds;
+             int ret;
+
+             if(_tdm_tick_schedule() != TDM_ERROR_NONE)
+               goto done;
+
+             FD_ZERO(&rfds);
+             FD_SET(tdm_fd, &rfds);
+
+             ret = poll(&fds, 1, _tdm_req_fps);
+
+             if ((ret == 1) && (FD_ISSET(tdm_fd, &rfds)))
+               {
+                  fail_cnt = 0;
+                  err = tdm_client_handle_events(client);
+                  if (err != TDM_ERROR_NONE) {
+                       ERR("tdm_client_handle_events failed err %d\n", err);
+                       vblank_wait = 0;
+                       _tdm_send_time(ecore_time_get());
+                  }
+               }
+             else
+               {
+                  if ((err < 0) && (errno != EINTR) && (errno != EAGAIN))  /* normal case */
+                    goto done;
+
+                  fail_cnt = 0;
+                  vblank_wait = 0;
+                  _tdm_send_time(ecore_time_get());
+               }
+
+          }
+        else
+          {
+             fail_cnt++;
+             if (fail_cnt > 1000)
+               {
+                  vblank_wait = 0;
+                  _tdm_send_time(ecore_time_get());
+                  ERR("tdm vblank handler issue\n");
+                  fail_cnt = 0;
+               }
+          }
+     }
+
+done:
+   if (vblank)
+     tdm_client_vblank_destroy(vblank);
+   if (client)
+     tdm_client_destroy(client);
+   tdm_fd = -1;
+   vblank_wait = 0;
+   vblank = client = output = NULL;
+
+   return;
+
+}
+
+static void
+_tdm_tick_notify(void *data EINA_UNUSED, Ecore_Thread *thread EINA_UNUSED, void *msg)
+{
+   int tick_queued;
+
+   eina_spinlock_take(&tick_queue_lock);
+   tick_queued = tick_queue_count;
+   tick_queue_count--;
+   eina_spinlock_release(&tick_queue_lock);
+   DBG("notify.... %3.3f %i", *((double *)msg), tdm_event_is_busy);
+
+   if (tdm_event_is_busy)
+     {
+        double *t = msg;
+        static double pt = 0.0;
+
+        DBG("VSYNC %1.8f = delt %1.8f", *t, *t - pt);
+        if ((!tick_skip) && (tick_queued == 1) && (*t > pt))
+          {
+             ecore_loop_time_set(*t);
+             ecore_animator_custom_tick();
+             pt = *t;
+          }
+        else if (tick_queued > 10)
+          {
+             DBG("skip this vsync for schedule queued %d\n", tick_queued);
+          }
+     }
+   free(msg);
+}
+
+static Eina_Bool
+_tdm_client_init(void)
+{
+   tdm_error error;
+   double fps;
+
+   if (!client)
+     {
+        client = tdm_client_create(&error);
+        if (error != TDM_ERROR_NONE) {
+             ERR("tdm_client_create error %d\n", error);
+             goto done;
+        }
+
+        error = tdm_client_get_fd(client, &tdm_fd);
+        if (error != TDM_ERROR_NONE) {
+             ERR("tdm_client_get_fd error %d\n", error);
+             goto done;
+        }
+
+        output = tdm_client_get_output(client, NULL, &error);
+        if (error != TDM_ERROR_NONE)
+          {
+             ERR("tdm_client_get_output error %d\n", error);
+             goto done;
+          }
+
+
+        vblank = tdm_client_output_create_vblank(output, &error);
+        if (error != TDM_ERROR_NONE) {
+             ERR("tdm_client_output_create_vblank error %d\n", error);
+             goto done;
+        }
+
+        tdm_client_vblank_set_enable_fake(vblank, 1);
+     }
+
+   fps = ecore_animator_frametime_get();
+   if (_tdm_req_fps != fps * 1000)
+     _tdm_req_fps = fps * 1000;
+   tdm_client_vblank_set_fps(vblank, _tdm_req_fps);
+
+   return EINA_TRUE;
+
+done:
+   if (vblank) tdm_client_vblank_destroy(vblank);
+   if (client) tdm_client_destroy(client);
+   vblank = client = output = NULL;
+   tdm_fd = -1;
+   return EINA_FALSE;
+}
+
+static void
+_tdm_tick_finished(void *data EINA_UNUSED, Ecore_Thread *thread EINA_UNUSED)
+{
+
+   if (vblank)
+     tdm_client_vblank_destroy(vblank);
+
+   if (client)
+     tdm_client_destroy(client);
+
+   if (_vsync_log_dom > 0)
+        eina_log_domain_unregister(_vsync_log_dom);
+
+   _vsync_log_dom = -1;
+   tdm_fd = -1;
+   vblank_wait = 0;
+   vblank = client = output = NULL;
+
+   eina_spinlock_free(&tick_queue_lock);
+   eina_thread_queue_free(thq);
+   tick_queue_count = 0;
+   tdm_thread = NULL;
+   thq = NULL;
+}
+
+void
+_ecore_wl_animator_vsync_tick_quit(EINA_UNUSED void *data)
+{
+   int i;
+   _tick_send(-1);
+
+   if (!thq) return;
+
+   for (i = 0; (i < 500) && (tdm_thread); i++)
+     {
+        usleep(1000);
+     }
+}
+
+void
+_ecore_wl_animator_vsync_tick_begin(EINA_UNUSED void *data)
+{
+   if (_vsync_log_dom < 0)
+     _vsync_log_dom = eina_log_domain_register("ecore_anim_vsync", EINA_COLOR_LIGHTRED);
+
+   if (_tdm_client_init())
+     {
+        // if (getenv("ECORE_ANIMATOR_SKIP")) tick_skip = EINA_TRUE;
+        tdm_event_is_busy = 1;
+        if (!tdm_thread)
+          {
+             eina_spinlock_new(&tick_queue_lock);
+             thq = eina_thread_queue_new();
+             tdm_thread = ecore_thread_feedback_run(_tdm_tick_core,
+                                                    _tdm_tick_notify,
+                                                    _tdm_tick_finished,
+                                                    _tdm_tick_finished,
+                                                    NULL, EINA_TRUE);
+          }
+        tick_queue_count = 0;
+        _tick_send(1);
+     }
+   else
+     ERR("tdm tick begin failed\n");
+}
+
+void
+_ecore_wl_animator_vsync_tick_end(EINA_UNUSED void *data)
+{
+   tdm_event_is_busy = 0;
+   tick_queue_count = 0;
+   _tick_send(0);
+}
+
index f059b23..1de69d6 100644 (file)
@@ -426,4 +426,10 @@ struct wl_subcompositor *_ecore_wl_subcompositor_get(void);
 void _ecore_wl_input_device_manager_setup(unsigned int id);
 int ecore_wl_keycode_from_keysym(struct xkb_keymap *keymap, xkb_keysym_t keysym, xkb_keycode_t **keycodes);
 
+/* TIZEN_ONLY : To use vsync as a custom source of animator */
+void _ecore_wl_animator_vsync_tick_begin(void *data);
+void _ecore_wl_animator_vsync_tick_end(void *data);
+void _ecore_wl_animator_vsync_tick_quit(void *data);
+/* TIZEN_ONLY : To use vsync as a custom source of animator */
+
 #endif