From a7b4a3c12d737462b09a71f25c5624f1489dd659 Mon Sep 17 00:00:00 2001 From: Leandro Pereira Date: Tue, 18 Dec 2012 16:26:44 +0000 Subject: [PATCH] evas: Async render SVN revision: 81282 --- src/lib/evas/Evas.h | 39 +++++ src/lib/evas/canvas/evas_async_events.c | 75 ++++++--- src/lib/evas/canvas/evas_main.c | 4 + src/lib/evas/canvas/evas_render.c | 193 +++++++++++++++++++++--- src/lib/evas/include/evas_common.h | 3 + src/lib/evas/include/evas_private.h | 11 +- 6 files changed, 280 insertions(+), 45 deletions(-) diff --git a/src/lib/evas/Evas.h b/src/lib/evas/Evas.h index 8e59b8ebdb..4f7da82627 100644 --- a/src/lib/evas/Evas.h +++ b/src/lib/evas/Evas.h @@ -1670,6 +1670,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. * @@ -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) diff --git a/src/lib/evas/canvas/evas_async_events.c b/src/lib/evas/canvas/evas_async_events.c index 172b20f844..209ba8e854 100644 --- a/src/lib/evas/canvas/evas_async_events.c +++ b/src/lib/evas/canvas/evas_async_events.c @@ -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; + int ret; - if (_fd_read == -1) return 0; - - _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) { diff --git a/src/lib/evas/canvas/evas_main.c b/src/lib/evas/canvas/evas_main.c index 8251ed0e3f..db44c3ec98 100644 --- a/src/lib/evas/canvas/evas_main.c +++ b/src/lib/evas/canvas/evas_main.c @@ -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 }; diff --git a/src/lib/evas/canvas/evas_render.c b/src/lib/evas/canvas/evas_render.c index 85f6be526d..eb4f823a3b 100644 --- a/src/lib/evas/canvas/evas_render.c +++ b/src/lib/evas/canvas/evas_render.c @@ -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 diff --git a/src/lib/evas/include/evas_common.h b/src/lib/evas/include/evas_common.h index 62df43c722..5b231ace7a 100644 --- a/src/lib/evas/include/evas_common.h +++ b/src/lib/evas/include/evas_common.h @@ -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); diff --git a/src/lib/evas/include/evas_private.h b/src/lib/evas/include/evas_private.h index 8f0e14daec..8f68171c2e 100644 --- a/src/lib/evas/include/evas_private.h +++ b/src/lib/evas/include/evas_private.h @@ -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);