diff --git a/src/Makefile_Evas.am b/src/Makefile_Evas.am index bb80008340..41372e6e9e 100644 --- a/src/Makefile_Evas.am +++ b/src/Makefile_Evas.am @@ -75,6 +75,7 @@ lib/evas/canvas/evas_object_grid.c \ lib/evas/canvas/evas_font_dir.c \ lib/evas/canvas/evas_rectangle.c \ lib/evas/canvas/evas_render.c \ +lib/evas/canvas/evas_render2.c \ lib/evas/canvas/evas_smart.c \ lib/evas/canvas/evas_stack.c \ lib/evas/canvas/evas_async_events.c \ diff --git a/src/lib/evas/canvas/evas_render.c b/src/lib/evas/canvas/evas_render.c index d33cd42421..7d3491a444 100644 --- a/src/lib/evas/canvas/evas_render.c +++ b/src/lib/evas/canvas/evas_render.c @@ -10,6 +10,17 @@ #include #endif +Eina_Bool +_evas_render2_begin(Eo *eo_e, Eina_Bool make_updates, + Eina_Bool do_draw, Eina_Bool do_async); +void +_evas_render2_idle_flush(Eo *eo_e); +void +_evas_render2_dump(Eo *eo_e); +void +_evas_render2_wait(Eo *eo_e); + + /* debug rendering * NOTE: Define REND_DBG 1 in evas_private.h to enable debugging. Don't define * it here since the flag is used on other places too. */ @@ -2279,9 +2290,18 @@ _canvas_render_async(Eo *eo_e, void *_pd, va_list *list) { Eina_Bool *ret = va_arg(*list, Eina_Bool *); Evas_Public_Data *e = _pd; + static int render_2 = -1; - *ret = evas_render_updates_internal(eo_e, 1, 1, evas_render_pipe_wakeup, - e, EINA_TRUE); + if (render_2 == -1) + { + if (getenv("EVAS_RENDER2")) render_2 = 1; + else render_2 = 0; + } + if (render_2) + *ret = _evas_render2_begin(eo_e, EINA_TRUE, EINA_TRUE, EINA_TRUE); + else + *ret = evas_render_updates_internal(eo_e, 1, 1, evas_render_pipe_wakeup, + e, EINA_TRUE); } EAPI Eina_List * @@ -2302,10 +2322,24 @@ evas_render_updates_internal_wait(Evas *eo_e, { Eina_List *ret = NULL; Evas_Public_Data *e = eo_data_scope_get(eo_e, EVAS_CLASS); + static int render_2 = -1; - if (!evas_render_updates_internal(eo_e, make_updates, do_draw, NULL, - NULL, EINA_FALSE)) - return NULL; + if (render_2 == -1) + { + if (getenv("EVAS_RENDER2")) render_2 = 1; + else render_2 = 0; + } + if (render_2) + { + if (!_evas_render2_begin(eo_e, make_updates, do_draw, EINA_FALSE)) + return NULL; + } + else + { + 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; @@ -2374,26 +2408,40 @@ evas_render_idle_flush(Evas *eo_e) void _canvas_render_idle_flush(Eo *eo_e, void *_pd, va_list *list EINA_UNUSED) { - Evas_Public_Data *e = _pd; + static int render_2 = -1; - evas_render_rendering_wait(e); - - evas_fonts_zero_pressure(eo_e); + if (render_2 == -1) + { + if (getenv("EVAS_RENDER2")) render_2 = 1; + else render_2 = 0; + } + if (render_2) + { + _evas_render2_idle_flush(eo_e); + } + else + { + Evas_Public_Data *e = _pd; - if ((e->engine.func) && (e->engine.func->output_idle_flush) && - (e->engine.data.output)) - e->engine.func->output_idle_flush(e->engine.data.output); - - OBJS_ARRAY_FLUSH(&e->active_objects); - OBJS_ARRAY_FLUSH(&e->render_objects); - OBJS_ARRAY_FLUSH(&e->restack_objects); - OBJS_ARRAY_FLUSH(&e->delete_objects); - OBJS_ARRAY_FLUSH(&e->obscuring_objects); - OBJS_ARRAY_FLUSH(&e->temporary_objects); - eina_array_foreach(&e->clip_changes, _evas_clip_changes_free, NULL); - eina_array_clean(&e->clip_changes); - - e->invalidate = EINA_TRUE; + evas_render_rendering_wait(e); + + evas_fonts_zero_pressure(eo_e); + + if ((e->engine.func) && (e->engine.func->output_idle_flush) && + (e->engine.data.output)) + e->engine.func->output_idle_flush(e->engine.data.output); + + OBJS_ARRAY_FLUSH(&e->active_objects); + OBJS_ARRAY_FLUSH(&e->render_objects); + OBJS_ARRAY_FLUSH(&e->restack_objects); + OBJS_ARRAY_FLUSH(&e->delete_objects); + OBJS_ARRAY_FLUSH(&e->obscuring_objects); + OBJS_ARRAY_FLUSH(&e->temporary_objects); + eina_array_foreach(&e->clip_changes, _evas_clip_changes_free, NULL); + eina_array_clean(&e->clip_changes); + + e->invalidate = EINA_TRUE; + } } EAPI void @@ -2406,7 +2454,17 @@ void _canvas_sync(Eo *eo_e, void *_pd EINA_UNUSED, va_list *list EINA_UNUSED) { Evas_Public_Data *e = eo_data_scope_get(eo_e, EVAS_CLASS); - evas_render_rendering_wait(e); + static int render_2 = -1; + + if (render_2 == -1) + { + if (getenv("EVAS_RENDER2")) render_2 = 1; + else render_2 = 0; + } + if (render_2) + _evas_render2_wait(eo_e); + else + evas_render_rendering_wait(e); } void @@ -2444,66 +2502,80 @@ void _canvas_render_dump(Eo *eo_e EINA_UNUSED, void *_pd, va_list *list EINA_UNUSED) { Evas_Public_Data *e = _pd; - Evas_Layer *lay; + static int render_2 = -1; - evas_all_sync(); - evas_cache_async_freeze(); - - EINA_INLIST_FOREACH(e->layers, lay) + if (render_2 == -1) { - Evas_Object_Protected_Data *obj; - - EINA_INLIST_FOREACH(lay->objects, obj) - { - if (obj->proxy) - { - EINA_COW_WRITE_BEGIN(evas_object_proxy_cow, obj->proxy, Evas_Object_Proxy_Data, proxy_write) - { - if (proxy_write->surface) - { - e->engine.func->image_map_surface_free(e->engine.data.output, - proxy_write->surface); - proxy_write->surface = NULL; - } - } - EINA_COW_WRITE_END(evas_object_proxy_cow, obj->proxy, proxy_write); - } - if ((obj->type) && (!strcmp(obj->type, "image"))) - evas_object_inform_call_image_unloaded(obj->object); - _evas_render_dump_map_surfaces(obj->object); - } + if (getenv("EVAS_RENDER2")) render_2 = 1; + else render_2 = 0; } - if ((e->engine.func) && (e->engine.func->output_dump) && - (e->engine.data.output)) - e->engine.func->output_dump(e->engine.data.output); + if (render_2) + { + _evas_render2_dump(eo_e); + } + else + { + Evas_Layer *lay; + + evas_all_sync(); + evas_cache_async_freeze(); + + EINA_INLIST_FOREACH(e->layers, lay) + { + Evas_Object_Protected_Data *obj; + + EINA_INLIST_FOREACH(lay->objects, obj) + { + if (obj->proxy) + { + EINA_COW_WRITE_BEGIN(evas_object_proxy_cow, obj->proxy, Evas_Object_Proxy_Data, proxy_write) + { + if (proxy_write->surface) + { + e->engine.func->image_map_surface_free(e->engine.data.output, + proxy_write->surface); + proxy_write->surface = NULL; + } + } + EINA_COW_WRITE_END(evas_object_proxy_cow, obj->proxy, proxy_write); + } + if ((obj->type) && (!strcmp(obj->type, "image"))) + evas_object_inform_call_image_unloaded(obj->object); + _evas_render_dump_map_surfaces(obj->object); + } + } + if ((e->engine.func) && (e->engine.func->output_dump) && + (e->engine.data.output)) + e->engine.func->output_dump(e->engine.data.output); -#define GC_ALL(Cow) \ +#define GC_ALL(Cow) \ while (eina_cow_gc(Cow)) - GC_ALL(evas_object_proxy_cow); - GC_ALL(evas_object_map_cow); - GC_ALL(evas_object_image_pixels_cow); - GC_ALL(evas_object_image_load_opts_cow); - GC_ALL(evas_object_image_state_cow); + GC_ALL(evas_object_proxy_cow); + GC_ALL(evas_object_map_cow); + GC_ALL(evas_object_image_pixels_cow); + GC_ALL(evas_object_image_load_opts_cow); + GC_ALL(evas_object_image_state_cow); + + evas_fonts_zero_pressure(eo_e); - evas_fonts_zero_pressure(eo_e); + if ((e->engine.func) && (e->engine.func->output_idle_flush) && + (e->engine.data.output)) + e->engine.func->output_idle_flush(e->engine.data.output); - if ((e->engine.func) && (e->engine.func->output_idle_flush) && - (e->engine.data.output)) - e->engine.func->output_idle_flush(e->engine.data.output); - - OBJS_ARRAY_FLUSH(&e->active_objects); - OBJS_ARRAY_FLUSH(&e->render_objects); - OBJS_ARRAY_FLUSH(&e->restack_objects); - OBJS_ARRAY_FLUSH(&e->delete_objects); - OBJS_ARRAY_FLUSH(&e->obscuring_objects); - OBJS_ARRAY_FLUSH(&e->temporary_objects); - eina_array_foreach(&e->clip_changes, _evas_clip_changes_free, NULL); - eina_array_clean(&e->clip_changes); - - e->invalidate = EINA_TRUE; - - evas_cache_async_thaw(); + OBJS_ARRAY_FLUSH(&e->active_objects); + OBJS_ARRAY_FLUSH(&e->render_objects); + OBJS_ARRAY_FLUSH(&e->restack_objects); + OBJS_ARRAY_FLUSH(&e->delete_objects); + OBJS_ARRAY_FLUSH(&e->obscuring_objects); + OBJS_ARRAY_FLUSH(&e->temporary_objects); + eina_array_foreach(&e->clip_changes, _evas_clip_changes_free, NULL); + eina_array_clean(&e->clip_changes); + + e->invalidate = EINA_TRUE; + + evas_cache_async_thaw(); + } } void diff --git a/src/lib/evas/canvas/evas_render2.c b/src/lib/evas/canvas/evas_render2.c new file mode 100644 index 0000000000..1e56ccc803 --- /dev/null +++ b/src/lib/evas/canvas/evas_render2.c @@ -0,0 +1,374 @@ +#include "evas_common_private.h" +#include "evas_private.h" +#include +#include +#ifdef EVAS_CSERVE2 +#include "evas_cs2_private.h" +#endif + +#ifdef EVAS_RENDER_DEBUG_TIMING +#include +#endif + +////////////////////////////////////////////////////////////////////////////// +// this is the start of a rewrite of the evas rendering infra. first port of +// call is to just make it work and still support async rendering with no +// optimizations at all. once it WORKS properly start adding back +// optimizations one at a time very carefully until it is equivalent to where +// evas render was before, THEN... we can consider switching it on by default +// but until then it's off unless you set: +// +// export EVAS_RENDER2=1 +// +// at runtime. +////////////////////////////////////////////////////////////////////////////// + +// data types +////////////////////////////////////////////////////////////////////////////// +typedef struct Update Update; + +struct _Update +{ + Eina_Rectangle area; + void *surface; +}; + +// funcs +////////////////////////////////////////////////////////////////////////////// +Eina_Bool _evas_render2_begin(Eo *eo_e, Eina_Bool make_updates, Eina_Bool do_draw, Eina_Bool do_async); +void _evas_render2_idle_flush(Eo *eo_e); +void _evas_render2_dump(Eo *eo_e); +void _evas_render2_wait(Eo *eo_e); + +static void _evas_render2_end(Eo *eo_e); + +static void _evas_render2_cow_gc(Eina_Cow *cow, int max); +static void _evas_render2_cow_all_gc(int max); +static void _evas_render2_all_sync(void); +static void _evas_render2_wakeup_cb(void *target, Evas_Callback_Type type, void *event_info); +static void _evas_render2_wakeup_send(void *data); +static void _evas_render2_always_call(Eo *eo_e, Evas_Callback_Type type, void *event_info); + +// global data (for rendering only) +////////////////////////////////////////////////////////////////////////////// +static Eina_List *_rendering = NULL; + +////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////// +// BEGIN RENDERING (in mainloop) +/////////////////////////////////////////////////////////////////////// +Eina_Bool +_evas_render2_begin(Eo *eo_e, Eina_Bool make_updates, + Eina_Bool do_draw, Eina_Bool do_async) +{ + Evas_Public_Data *e = eo_data_scope_get(eo_e, EVAS_CLASS); + Eina_Rectangle *r; + Eina_List *l; + + // if nothing changed at all since last render - skip this frame + if (!e->changed) return EINA_FALSE; + // we are still rendering while being asked to render - skip this frame + if (e->rendering && do_async) return EINA_FALSE; + // check viewport size is same as output - not allowed to differ + if ((e->output.w != e->viewport.w) || (e->output.h != e->viewport.h)) + ERR("viewport size != output size!"); + + // call canvas callbacks saying we are in the pre-render state + _evas_render2_always_call(eo_e, EVAS_CALLBACK_RENDER_PRE, NULL); + // we have to calculate smare objects before render so do that here + evas_call_smarts_calculate(eo_e); + + // if the output size changed, add a full redraw + if ((e->output.changed) || (e->framespace.changed)) + { + e->engine.func->output_resize(e->engine.data.output, + e->output.w, e->output.h); + e->engine.func->output_redraws_rect_add(e->engine.data.output, 0, 0, + e->output.w, e->output.h); + } + // if there are explicit update regions - add them + EINA_LIST_FREE(e->damages, r) + { + // if we didnt just do a full redraw if output changed + if ((!e->output.changed) && (!e->framespace.changed)) + e->engine.func->output_redraws_rect_add(e->engine.data.output, + r->x, r->y, r->w, r->h); + eina_rectangle_free(r); + } + // remove obscures from rendering - we keep them around + EINA_LIST_FOREACH(e->obscures, l, r) + e->engine.func->output_redraws_rect_del(e->engine.data.output, + r->x, r->y, r->w, r->h); + + // we are actually asked to draw not just go through the motions for gc + if (do_draw) + { + // XXX: + // XXX: process all objects figuring out update regions + // XXX: + + // XXX: + // XXX: RENDER HERE! + if (do_async) + { + // XXX: send off render commands + } + else + { + // XXX: do render that is sent of above right here + } + // XXX: + + // if we are async... + if (do_async) + { + // ref the canvas so it stays while threads wortk + eo_ref(eo_e); + // track hanvas in list of things going in the background + e->rendering = EINA_TRUE; + _rendering = eina_list_append(_rendering, eo_e); + // flush the thread queue + evas_thread_queue_flush + ((Evas_Thread_Command_Cb)_evas_render2_wakeup_send, eo_e); + } + // if not async but we had actual update regions drawn + else if (e->render.updates) + { + // call output flush and callbacks around it + _evas_render2_always_call(eo_e, EVAS_CALLBACK_RENDER_FLUSH_PRE, + NULL); + e->engine.func->output_flush(e->engine.data.output, + EVAS_RENDER_MODE_SYNC); + _evas_render2_always_call(eo_e, EVAS_CALLBACK_RENDER_FLUSH_POST, + NULL); + } + } + + // reset flags sinc rendering is processed now + e->changed = EINA_FALSE; + e->viewport.changed = EINA_FALSE; + e->output.changed = EINA_FALSE; + e->framespace.changed = EINA_FALSE; + e->invalidate = EINA_FALSE; + + // XXX: + // XXX: delete objects no longer needed here + // XXX: + + // if we are not going to be async then do post render here + if (!do_async) + { + // clear our previous rendering stuff from the engine + e->engine.func->output_redraws_clear(e->engine.data.output); + // call the post render callback with info if appropriate + if (e->render.updates) + { + Evas_Event_Render_Post post; + + post.updated_area = e->render.updates; + _evas_render2_always_call(eo_e, EVAS_CALLBACK_RENDER_POST, &post); + } + else + _evas_render2_always_call(eo_e, EVAS_CALLBACK_RENDER_POST, NULL); + // clean out modules we don't need anymore + evas_module_clean(); + // clean out updates and tmp surfaces we were holding/tracking + if (!make_updates) + { + Update *u; + + EINA_LIST_FREE(e->render.updates, u) + { + //evas_cache_image_drop(u->surface); + free(u); + } + } + } + return EINA_TRUE; +} + +/////////////////////////////////////////////////////////////////////// +// END RENDERING (in mainloop) +/////////////////////////////////////////////////////////////////////// +static void +_evas_render2_end(Eo *eo_e) +{ + // this is actually called if rendering was async and is done. this is + // run in the mainloop where rendering began and may handle any cleanup + // or pixel upload if needed here + Evas_Public_Data *e = eo_data_scope_get(eo_e, EVAS_CLASS); + Eina_Bool have_updates = EINA_FALSE; + Update *u; + + // XXX: + // XXX: actually update screen from mainloop here if needed - eg software + // engine needs to xshmputimage here + // XXX: + + // clean out updates and tmp surfaces we were holding/tracking + if (e->render.updates) + { + have_updates = EINA_TRUE; + EINA_LIST_FREE(e->render.updates, u) + { + //evas_cache_image_drop(u->surface); + free(u); + } + } + // if we did do rendering flush output to target and call callbacks + if (have_updates) + { + _evas_render2_always_call(eo_e, EVAS_CALLBACK_RENDER_FLUSH_PRE, NULL); + e->engine.func->output_flush(e->engine.data.output, + EVAS_RENDER_MODE_ASYNC_END); + _evas_render2_always_call(eo_e, EVAS_CALLBACK_RENDER_FLUSH_POST, NULL); + } + // clear our previous rendering stuff from the engine + e->engine.func->output_redraws_clear(e->engine.data.output); + // stop tracking canvas as being async rendered + _rendering = eina_list_remove(_rendering, eo_e); + e->rendering = EINA_FALSE; + // call the post render callback with info if appropriate + if (e->render.updates) + { + Evas_Event_Render_Post post; + + post.updated_area = e->render.updates; + _evas_render2_always_call(eo_e, EVAS_CALLBACK_RENDER_POST, &post); + } + else + _evas_render2_always_call(eo_e, EVAS_CALLBACK_RENDER_POST, NULL); + // clean out modules we don't need anymore + evas_module_clean(); + // release canvas object ref + eo_unref(eo_e); +} + +/////////////////////////////////////////////////////////////////////// +// IDLE FLUSH (in mainloop) +/////////////////////////////////////////////////////////////////////// +void +_evas_render2_idle_flush(Eo *eo_e) +{ + Evas_Public_Data *e = eo_data_scope_get(eo_e, EVAS_CLASS); + + // wait for rendering to finish so we don't mess up shared resources + _evas_render2_wait(eo_e); + // clean fonts + evas_fonts_zero_pressure(eo_e); + // call engine idle flush call + if ((e->engine.func) && (e->engine.func->output_idle_flush) && + (e->engine.data.output)) + e->engine.func->output_idle_flush(e->engine.data.output); + // mark as invalidated + e->invalidate = EINA_TRUE; + // garbage collect up to 500 cow segments from our cow types + // not e that we should probably expose a call to do this outside of evas + // so ecore evas can call it in an idler + _evas_render2_cow_all_gc(500); +} + +/////////////////////////////////////////////////////////////////////// +// DUMP DATA (in mainloop) +/////////////////////////////////////////////////////////////////////// +void +_evas_render2_dump(Eo *eo_e) +{ + Evas_Public_Data *e = eo_data_scope_get(eo_e, EVAS_CLASS); + + // freeze core cache system async work + evas_cache_async_freeze(); + // wait for all canvases to render as they may share data we are dumping + _evas_render2_all_sync(); + // go through idle flush first + _evas_render2_idle_flush(eo_e); + // also now tell engine to dump too + if ((e->engine.func) && (e->engine.func->output_dump) && + (e->engine.data.output)) + e->engine.func->output_dump(e->engine.data.output); + // clean up all cow sections no matter how many + _evas_render2_cow_all_gc(0); + // unfreeze core cache system + evas_cache_async_thaw(); +} + +/////////////////////////////////////////////////////////////////////// +// WAIT ON CANVAS RENDER (if async, in mainloop) +/////////////////////////////////////////////////////////////////////// +void +_evas_render2_wait(Eo *eo_e) +{ + Evas_Public_Data *e = eo_data_scope_get(eo_e, EVAS_CLASS); + while (e->rendering) evas_async_events_process_blocking(); +} + + + + +// helpers +////////////////////////////////////////////////////////////////////////////// + +static void +_evas_render2_cow_gc(Eina_Cow *cow, int max) +{ + // gc a single cow type up to max iter or if max <= 0, all of them + int i = 0; + + while (eina_cow_gc(cow)) + { + if (max < 1) continue; + i++; + if (i > max) break; + } +} + +static void +_evas_render2_cow_all_gc(int max) +{ + // gc all known cow types + _evas_render2_cow_gc(evas_object_proxy_cow, max); + _evas_render2_cow_gc(evas_object_map_cow, max); + _evas_render2_cow_gc(evas_object_image_pixels_cow, max); + _evas_render2_cow_gc(evas_object_image_load_opts_cow, max); + _evas_render2_cow_gc(evas_object_image_state_cow, max); +} + +static void +_evas_render2_all_sync(void) +{ + // wait for ALL canvases to stop rendering + Eo *eo_e; + + if (!_rendering) return; + eo_e = eina_list_data_get(eina_list_last(_rendering)); + _evas_render2_wait(eo_e); +} + +static void +_evas_render2_wakeup_cb(void *target, Evas_Callback_Type type EINA_UNUSED, void *event_info EINA_UNUSED) +{ + // in mainloop run the rendering end handler + Eo *eo_e = target; + + _evas_render2_end(eo_e); +} + +static void +_evas_render2_wakeup_send(void *data) +{ + // pass an event to the mainloop async event handler in evas so mainloop + // runs wakeup_cb and not in any thread + evas_async_events_put(data, 0, NULL, _evas_render2_wakeup_cb); +} + +static void +_evas_render2_always_call(Eo *eo_e, Evas_Callback_Type type, void *event_info) +{ + int freeze_num = 0, i; + + eo_do(eo_e, eo_event_freeze_get(&freeze_num)); + for (i = 0; i < freeze_num; i++) eo_do(eo_e, eo_event_thaw()); + evas_event_callback_call(eo_e, type, event_info); + for (i = 0; i < freeze_num; i++) eo_do(eo_e, eo_event_freeze()); +}