evas: Async render
authorLeandro Pereira <leandro@profusion.mobi>
Tue, 18 Dec 2012 16:26:44 +0000 (16:26 +0000)
committerLeandro Pereira <leandro@profusion.mobi>
Tue, 18 Dec 2012 16:26:44 +0000 (16:26 +0000)
SVN revision: 81282

src/lib/evas/Evas.h
src/lib/evas/canvas/evas_async_events.c
src/lib/evas/canvas/evas_main.c
src/lib/evas/canvas/evas_render.c
src/lib/evas/include/evas_common.h
src/lib/evas/include/evas_private.h

index 8e59b8e..4f7da82 100644 (file)
@@ -1671,6 +1671,31 @@ EAPI void              evas_obscured_rectangle_add(Evas *e, int x, int y, int w,
 EAPI void              evas_obscured_clear(Evas *e) EINA_ARG_NONNULL(1);
 
 /**
+ * Render the given Evas canvas asynchronously.
+ *
+ * @param e The canvas to render.
+ * @param func Optional function to call with the list of updated areas.
+ * @param data User data to pass to @p func.
+ *
+ * @return EINA_TRUE if the canvas will render, EINA_FALSE otherwise.
+ *
+ * This function only returns EINA_TRUE whne a frame will be rendered. If the
+ * previous frame is still rendering, EINA_FALSE will be returned so the users
+ * know not to wait for the updates callback and just return to their main
+ * loop.
+ *
+ * If a @p func callback is given, a list of updated areas will be generated
+ * and the function will be called from the main thread after the rendered
+ * frame is flushed to the screen. The resulting list should be freed with
+ * @f evas_render_updates_free().
+ * The list is given in the @p event_info parameter of the callback function.
+ *
+ * @ingroup Evas_Canvas
+ * @since 1.8
+ */
+EAPI Eina_Bool         evas_render_async(Evas *e, Evas_Event_Cb func, void *data) EINA_ARG_NONNULL(1);
+
+/**
  * Force immediate renderization of the given Evas canvas.
  *
  * @param e The given canvas pointer.
@@ -2425,6 +2450,7 @@ enum
    EVAS_CANVAS_SUB_ID_OBJECTS_IN_RECTANGLE_GET,
    EVAS_CANVAS_SUB_ID_SMART_OBJECTS_CALCULATE,
    EVAS_CANVAS_SUB_ID_SMART_OBJECTS_CALCULATE_COUNT_GET,
+   EVAS_CANVAS_SUB_ID_RENDER_ASYNC,
    EVAS_CANVAS_SUB_ID_LAST
 };
 
@@ -3692,6 +3718,19 @@ enum
  */
 #define evas_canvas_smart_objects_calculate_count_get(ret) EVAS_CANVAS_ID(EVAS_CANVAS_SUB_ID_SMART_OBJECTS_CALCULATE_COUNT_GET), EO_TYPECHECK(int *, ret)
 
+/**
+ * @def evas_canvas_render_async
+ * @since 1.8
+ *
+ * Render canvas asynchronously
+ *
+ * @param[in] func Callback function for list of updates
+ * @param[in] data User data pointer to pass to func
+ * @param[out] ret Whether or not a frame will get rendered after the call
+ *
+ * @see evas_render_async
+ */
+#define evas_canvas_render_async(func, data, ret) EVAS_CANVAS_ID(EVAS_CANVAS_SUB_ID_RENDER_ASYNC), EO_TYPECHECK(Evas_Event_Cb, func), EO_TYPECHECK(void *, data), EO_TYPECHECK(Eina_Bool *, ret)
 
 
 
index 172b20f..209ba8e 100644 (file)
@@ -105,33 +105,20 @@ evas_async_events_fd_get(void)
    return _fd_read;
 }
 
-EAPI int
-evas_async_events_process(void)
+static int
+_evas_async_events_process_single(void)
 {
    Evas_Event_Async *ev;
-   int check;
-   int count = 0;
-
-   if (_fd_read == -1) return 0;
+   int ret;
 
-   _evas_async_events_fork_handle();
-   
-   do
+   ret = read(_fd_read, &ev, sizeof(Evas_Event_Async *));
+   if (ret == sizeof(Evas_Event_Async *))
      {
-       check = read(_fd_read, &ev, sizeof (Evas_Event_Async *));
-
-       if (check == sizeof (Evas_Event_Async *))
-         {
-             if (ev->func) ev->func((void *)ev->target, ev->type, ev->event_info);
-            free(ev);
-            count++;
-         }
+        if (ev->func) ev->func((void *)ev->target, ev->type, ev->event_info);
+        free(ev);
+        return 1;
      }
-   while (check > 0);
-
-   evas_cache_image_wakeup();
-
-   if (check < 0)
+   else if (ret < 0)
      {
         switch (errno)
           {
@@ -139,13 +126,55 @@ evas_async_events_process(void)
            case EINVAL:
            case EIO:
            case EISDIR:
-             _fd_read = -1;
+              _fd_read = -1;
           }
      }
 
+   return ret;
+}
+
+EAPI int
+evas_async_events_process(void)
+{
+   int count = 0;
+
+   if (_fd_read == -1) return 0;
+
+   _evas_async_events_fork_handle();
+
+   while (_evas_async_events_process_single() > 0) count++;
+
+   evas_cache_image_wakeup();
+
    return count;
 }
 
+static void
+_evas_async_events_fd_blocking_set(Eina_Bool blocking)
+{
+   long flags = fcntl(_fd_read, F_GETFL);
+
+   if (blocking) flags &= ~O_NONBLOCK;
+   else flags |= O_NONBLOCK;
+
+   fcntl(_fd_read, F_SETFL, flags);
+}
+
+int
+evas_async_events_process_blocking(void)
+{
+   int ret;
+
+   _evas_async_events_fork_handle();
+
+   _evas_async_events_fd_blocking_set(EINA_TRUE);
+   ret = _evas_async_events_process_single();
+   evas_cache_image_wakeup(); /* FIXME: is this needed ? */
+   _evas_async_events_fd_blocking_set(EINA_FALSE);
+
+   return ret;
+}
+
 EAPI Eina_Bool
 evas_async_events_put(const void *target, Evas_Callback_Type type, void *event_info, Evas_Async_Events_Put_Cb func)
 {
index 8251ed0..db44c3e 100644 (file)
@@ -152,6 +152,8 @@ evas_free(Evas *eo_e)
    MAGIC_CHECK(eo_e, Evas, MAGIC_EVAS);
    return;
    MAGIC_CHECK_END();
+   Evas_Public_Data *e = eo_data_get(eo_e, EVAS_CLASS);
+   e->requested_free = EINA_TRUE;
    eo_unref(eo_e);
 }
 
@@ -1064,6 +1066,7 @@ _class_constructor(Eo_Class *klass)
         EO_OP_FUNC(EVAS_CANVAS_ID(EVAS_CANVAS_SUB_ID_OBJECTS_IN_RECTANGLE_GET), _canvas_objects_in_rectangle_get),
         EO_OP_FUNC(EVAS_CANVAS_ID(EVAS_CANVAS_SUB_ID_SMART_OBJECTS_CALCULATE), _canvas_smart_objects_calculate),
         EO_OP_FUNC(EVAS_CANVAS_ID(EVAS_CANVAS_SUB_ID_SMART_OBJECTS_CALCULATE_COUNT_GET), _canvas_smart_objects_calculate_count_get),
+        EO_OP_FUNC(EVAS_CANVAS_ID(EVAS_CANVAS_SUB_ID_RENDER_ASYNC), _canvas_render_async),
         EO_OP_FUNC_SENTINEL
    };
 
@@ -1163,6 +1166,7 @@ static const Eo_Op_Description op_desc[] = {
      EO_OP_DESCRIPTION(EVAS_CANVAS_SUB_ID_OBJECTS_IN_RECTANGLE_GET, "Retrieves the objects in the given rectangle region."),
      EO_OP_DESCRIPTION(EVAS_CANVAS_SUB_ID_SMART_OBJECTS_CALCULATE, "Call user-provided calculate() smart functions."),
      EO_OP_DESCRIPTION(EVAS_CANVAS_SUB_ID_SMART_OBJECTS_CALCULATE_COUNT_GET, "Get the internal counter that counts the number of smart calculations."),
+     EO_OP_DESCRIPTION(EVAS_CANVAS_SUB_ID_RENDER_ASYNC, "Renders the canvas asynchronously."),
      EO_OP_DESCRIPTION_SENTINEL
 };
 
index 85f6be5..eb4f823 100644 (file)
@@ -1291,15 +1291,23 @@ _evas_render_cutout_add(Evas *eo_e, Evas_Object *eo_obj, int off_x, int off_y)
      }
 }
 
-static Eina_List *
+void
+evas_render_rendering_wait(Evas_Public_Data *evas)
+{
+   while (evas->rendering) evas_async_events_process_blocking();
+}
+
+static Eina_Bool
 evas_render_updates_internal(Evas *eo_e,
                              unsigned char make_updates,
-                             unsigned char do_draw)
+                             unsigned char do_draw,
+                             Evas_Render_Done_Cb done_func,
+                             void *done_data,
+                             Eina_Bool do_async)
 {
    Evas_Object *eo_obj;
    Evas_Object_Protected_Data *obj;
    Evas_Public_Data *e;
-   Eina_List *updates = NULL;
    Eina_List *ll;
    void *surface;
    Eina_Bool clean_them = EINA_FALSE;
@@ -1312,11 +1320,16 @@ evas_render_updates_internal(Evas *eo_e,
    Eina_Bool haveup = 0;
 
    MAGIC_CHECK(eo_e, Evas, MAGIC_EVAS);
-   return NULL;
+   return EINA_FALSE;
    MAGIC_CHECK_END();
 
    e = eo_data_get(eo_e, EVAS_CLASS);
-   if (!e->changed) return NULL;
+   if (!e->changed) return EINA_FALSE;
+
+   if (e->rendering) return EINA_FALSE;
+   e->rendering = EINA_TRUE;
+
+   if (do_async) eo_ref(eo_e);
 
 #ifdef EVAS_CSERVE2
    if (evas_cserve2_use_get())
@@ -1537,15 +1550,25 @@ evas_render_updates_internal(Evas *eo_e,
                  &cx, &cy, &cw, &ch)))
           {
              int off_x, off_y;
+             Render_Updates *ru;
 
              RD("  [--- UPDATE %i %i %ix%i\n", ux, uy, uw, uh);
-             if (make_updates)
+             if (do_async)
+               {
+                  ru = malloc(sizeof(*ru));
+                  ru->surface = surface;
+                  NEW_RECT(ru->area, ux, uy, uw, uh);
+                  e->render.updates = eina_list_append(e->render.updates, ru);
+                  evas_cache_image_ref(surface);
+               }
+             else if (make_updates)
                {
                   Eina_Rectangle *rect;
 
                   NEW_RECT(rect, ux, uy, uw, uh);
                   if (rect)
-                    updates = eina_list_append(updates, rect);
+                     e->render.updates = eina_list_append(e->render.updates,
+                                                          rect);
                }
              haveup = EINA_TRUE;
              off_x = cx - ux;
@@ -1647,30 +1670,41 @@ evas_render_updates_internal(Evas *eo_e,
 #ifdef REND_DBG
                                                              , 1
 #endif
-                                                            );
+                                                             , do_async);
                             e->engine.func->context_cutout_clear(e->engine.data.output,
                                                                  e->engine.data.context);
                          }
                     }
                }
-             /* punch rect out */
-             e->engine.func->output_redraws_next_update_push(e->engine.data.output,
-                                                             surface,
-                                                             ux, uy, uw, uh);
+
+             if (!do_async)
+                e->engine.func->output_redraws_next_update_push(e->engine.data.output,
+                                                                surface,
+                                                                ux, uy, uw, uh);
+
              /* free obscuring objects list */
              eina_array_clean(&e->temporary_objects);
              RD("  ---]\n");
           }
-        /* flush redraws */
-        if (haveup)
+
+        if (do_async)
+          {
+             evas_thread_queue_flush((Evas_Thread_Command_Cb)done_func, done_data, 0);
+          }
+        else if (haveup)
           {
              evas_event_callback_call(eo_e, EVAS_CALLBACK_RENDER_FLUSH_PRE, NULL);
              e->engine.func->output_flush(e->engine.data.output);
              evas_event_callback_call(eo_e, EVAS_CALLBACK_RENDER_FLUSH_POST, NULL);
           }
      }
-   /* clear redraws */
-   e->engine.func->output_redraws_clear(e->engine.data.output);
+
+   if (!do_async)
+     {
+        /* clear redraws */
+        e->engine.func->output_redraws_clear(e->engine.data.output);
+     }
+
    /* and do a post render pass */
    for (i = 0; i < e->active_objects.count; ++i)
      {
@@ -1753,11 +1787,85 @@ evas_render_updates_internal(Evas *eo_e,
 
    evas_module_clean();
 
-   evas_event_callback_call(eo_e, EVAS_CALLBACK_RENDER_POST, NULL);
+   if (!do_async)
+      evas_event_callback_call(eo_e, EVAS_CALLBACK_RENDER_POST, NULL);
 
    RD("---]\n");
 
-   return updates;
+   return EINA_TRUE;
+}
+
+static void
+evas_render_wakeup(Evas *eo_e)
+{
+   Render_Updates *ru;
+   Eina_Bool haveup = EINA_FALSE;
+   Eina_List *ret_updates = NULL;
+   Evas_Public_Data *e = eo_data_get(eo_e, EVAS_CLASS);
+
+   if (e->requested_free)
+     {
+        EINA_LIST_FREE(e->render.updates, ru)
+          {
+             eina_rectangle_free(ru->area);
+             free(ru);
+          }
+        goto end;
+     }
+
+   EINA_LIST_FREE(e->render.updates, ru)
+     {
+        /* punch rect out */
+        e->engine.func->output_redraws_next_update_push(e->engine.data.output,
+                                                        ru->surface,
+                                                        ru->area->x,
+                                                        ru->area->y,
+                                                        ru->area->w,
+                                                        ru->area->h);
+        if (e->render.updates_cb)
+           ret_updates = eina_list_append(ret_updates, ru->area);
+        else
+           eina_rectangle_free(ru->area);
+        evas_cache_image_drop(ru->surface);
+        free(ru);
+        haveup = EINA_TRUE;
+     }
+
+   /* flush redraws */
+   if (haveup)
+     {
+        evas_event_callback_call(eo_e, EVAS_CALLBACK_RENDER_FLUSH_PRE, NULL);
+        e->engine.func->output_flush(e->engine.data.output);
+        evas_event_callback_call(eo_e, EVAS_CALLBACK_RENDER_FLUSH_POST, NULL);
+     }
+
+   /* clear redraws */
+   e->engine.func->output_redraws_clear(e->engine.data.output);
+
+   evas_event_callback_call(eo_e, EVAS_CALLBACK_RENDER_POST, NULL);
+
+   if (e->render.updates_cb)
+      e->render.updates_cb(e->render.data, eo_e, ret_updates);
+
+   e->rendering = EINA_FALSE;
+   e->render.updates_cb = NULL;
+   e->render.data = NULL;
+
+end:
+   eo_unref(eo_e);
+}
+
+static void
+evas_render_async_wakeup(void *target, Evas_Callback_Type type EINA_UNUSED, void *event_info EINA_UNUSED)
+{
+   Evas_Public_Data *e = target;
+   evas_render_wakeup(e->evas);
+}
+
+static void
+evas_render_pipe_wakeup(void *data)
+{
+   evas_async_events_put(data, 0, NULL, evas_render_async_wakeup);
 }
 
 EAPI void
@@ -1769,6 +1877,30 @@ evas_render_updates_free(Eina_List *updates)
       eina_rectangle_free(r);
 }
 
+EAPI Eina_Bool
+evas_render_async(Evas *eo_e, Evas_Event_Cb func, void *data)
+{
+   MAGIC_CHECK(eo_e, Evas, MAGIC_EVAS);
+   return EINA_FALSE;
+   MAGIC_CHECK_END();
+   Eina_Bool ret = EINA_FALSE;
+   eo_do(eo_e, evas_canvas_render_async(func, data, &ret));
+   return ret;
+}
+
+void
+_canvas_render_async(Eo *eo_e, void *_pd, va_list *list)
+{
+   Evas_Event_Cb func = va_arg(*list, Evas_Event_Cb);
+   void *data = va_arg(*list, void *);
+   Eina_Bool *ret = va_arg(*list, Eina_Bool *);
+   Evas_Public_Data *e = _pd;
+
+   e->render.updates_cb = func;
+   e->render.data = data;
+   *ret = evas_render_updates_internal(eo_e, 1, 1, evas_render_pipe_wakeup, e, EINA_TRUE);
+}
+
 EAPI Eina_List *
 evas_render_updates(Evas *eo_e)
 {
@@ -1780,6 +1912,25 @@ evas_render_updates(Evas *eo_e)
    return ret;
 }
 
+static Eina_List *
+evas_render_updates_internal_wait(Evas *eo_e,
+                                  unsigned char make_updates,
+                                  unsigned char do_draw)
+{
+   Eina_List *ret = NULL;
+   Evas_Public_Data *e = eo_data_get(eo_e, EVAS_CLASS);
+
+   if (!evas_render_updates_internal(eo_e, make_updates, do_draw, NULL, NULL,
+                                     EINA_FALSE))
+      return NULL;
+
+   ret = e->render.updates;
+   e->render.updates = NULL;
+   e->rendering = EINA_FALSE;
+
+   return ret;
+}
+
 void
 _canvas_render_updates(Eo *eo_e, void *_pd, va_list *list)
 {
@@ -1792,7 +1943,7 @@ _canvas_render_updates(Eo *eo_e, void *_pd, va_list *list)
         *ret = NULL;
         return;
      }
-   *ret = evas_render_updates_internal(eo_e, 1, 1);
+   *ret = evas_render_updates_internal_wait(eo_e, 1, 1);
 }
 
 EAPI void
@@ -1810,7 +1961,7 @@ _canvas_render(Eo *eo_e, void *_pd, va_list *list EINA_UNUSED)
    Evas_Public_Data *e = _pd;
 
    if (!e->changed) return;
-   evas_render_updates_internal(eo_e, 0, 1);
+   evas_render_updates_internal_wait(eo_e, 0, 1);
 }
 
 EAPI void
@@ -1826,7 +1977,7 @@ void
 _canvas_norender(Eo *eo_e, void *_pd EINA_UNUSED, va_list *list EINA_UNUSED)
 {
    //   if (!e->changed) return;
-   evas_render_updates_internal(eo_e, 0, 0);
+   evas_render_updates_internal_wait(eo_e, 0, 0);
 }
 
 EAPI void
index 62df43c..5b231ac 100644 (file)
@@ -1264,6 +1264,9 @@ Tilebuf_Rect *evas_common_regionbuf_rects_get (Regionbuf *rb);
 
 void              evas_font_dir_cache_free(void);
 
+int               evas_async_events_process_blocking(void);
+void              evas_render_rendering_wait(Evas_Public_Data *evas);
+
 void              evas_thread_init(void);
 void              evas_thread_shutdown(void);
 EAPI void         evas_thread_cmd_enqueue(Evas_Thread_Command_Cb cb, void *data, size_t size);
index 8f0e14d..8f68171 100644 (file)
@@ -371,6 +371,12 @@ struct _Evas_Public_Data
       int   info_magic;
    } engine;
 
+   struct {
+      Eina_List *updates;
+      Evas_Event_Cb updates_cb;
+      void *data;
+   } render;
+
    Eina_Array     delete_objects;
    Eina_Array     active_objects;
    Eina_Array     restack_objects;
@@ -414,6 +420,8 @@ struct _Evas_Public_Data
    unsigned char  cleanup : 1;
    unsigned char  focus : 1;
    Eina_Bool      is_frozen : 1;
+   Eina_Bool      rendering : 1;
+   Eina_Bool      requested_free : 1;
 
    Eina_List     *touch_points;
    Eina_List     *devices;
@@ -718,7 +726,7 @@ struct _Evas_Device
 struct _Evas_Object_Func
 {
    void (*free) (Evas_Object *obj, Evas_Object_Protected_Data *pd);
-   void (*render) (Evas_Object *obj, Evas_Object_Protected_Data *pd, void *output, void *context, void *surface, int x, int y);
+   void (*render) (Evas_Object *obj, Evas_Object_Protected_Data *pd, void *output, void *context, void *surface, int x, int y, Eina_Bool do_async);
    void (*render_pre) (Evas_Object *obj, Evas_Object_Protected_Data *pd);
    void (*render_post) (Evas_Object *obj, Evas_Object_Protected_Data *pd);
 
@@ -1127,6 +1135,7 @@ void _canvas_key_modifier_mask_get(Eo *e, void *_pd, va_list *list);
 void _canvas_damage_rectangle_add(Eo *obj, void *_pd, va_list *list);
 void _canvas_obscured_rectangle_add(Eo *obj, void *_pd, va_list *list);
 void _canvas_obscured_clear(Eo *obj, void *_pd, va_list *list);
+void _canvas_render_async(Eo *obj, void *_pd, va_list *list);
 void _canvas_render_updates(Eo *obj, void *_pd, va_list *list);
 void _canvas_render(Eo *e, void *_pd, va_list *list);
 void _canvas_norender(Eo *e, void *_pd, va_list *list);