raster pushed a commit to branch master. http://git.enlightenment.org/core/efl.git/commit/?id=cb841b56af11bbf787b36b3aaf6c6e69f1e1490f
commit cb841b56af11bbf787b36b3aaf6c6e69f1e1490f Author: Carsten Haitzler (Rasterman) <[email protected]> Date: Fri Dec 20 19:45:17 2013 +0900 evas render2 - more work on basics --- src/lib/evas/canvas/evas_render.c | 15 +- src/lib/evas/canvas/evas_render2.c | 459 +++++++++++++++++++++---------------- 2 files changed, 263 insertions(+), 211 deletions(-) diff --git a/src/lib/evas/canvas/evas_render.c b/src/lib/evas/canvas/evas_render.c index 6dab39c..a6b5768 100644 --- a/src/lib/evas/canvas/evas_render.c +++ b/src/lib/evas/canvas/evas_render.c @@ -10,15 +10,12 @@ #include <sys/time.h> #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); +// symbols of funcs from evas_render2 which is a next-gen replacement of +// the current evas render code. see evas_render2.c for more. +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 diff --git a/src/lib/evas/canvas/evas_render2.c b/src/lib/evas/canvas/evas_render2.c index ce95b02..f853473 100644 --- a/src/lib/evas/canvas/evas_render2.c +++ b/src/lib/evas/canvas/evas_render2.c @@ -40,49 +40,193 @@ 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); +static void _evas_render2_updates_clean(Evas_Public_Data *e); +static void _evas_render2_stage_last(Eo *eo_e, Eina_Bool make_updates); +static void _evas_render2_stage_generate_object_updates(Evas_Public_Data *e); +static void _evas_render2_stage_explicit_updates(Evas_Public_Data *e); +static void _evas_render2_stage_main_render_prepare(Evas_Public_Data *e); +static void _evas_render2_stage_render_do(Evas_Public_Data *e); +static void _evas_render2_stage_reset(Evas_Public_Data *e); +static void _evas_render2_stage_object_cleanup(Evas_Public_Data *e); +static void _evas_render2_th_render(void *data); +static void _evas_render2_end(Eo *eo_e); // global data (for rendering only) ////////////////////////////////////////////////////////////////////////////// static Eina_List *_rendering = NULL; +// actual helper/internal functions ////////////////////////////////////////////////////////////////////////////// +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; -/////////////////////////////////////////////////////////////////////// -// BEGIN RENDERING (in mainloop) -/////////////////////////////////////////////////////////////////////// -Eina_Bool -_evas_render2_begin(Eo *eo_e, Eina_Bool make_updates, - Eina_Bool do_draw, Eina_Bool do_async) + while (eina_cow_gc(cow)) + { + if (max < 1) continue; + i++; + if (i > max) break; + } +} + +static void +_evas_render2_cow_all_gc(int max) { - Evas_Public_Data *e = eo_data_scope_get(eo_e, EVAS_CLASS); - Eina_Rectangle *r; - Eina_List *l; + // 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 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!"); + if (!_rendering) return; + eo_e = eina_list_data_get(eina_list_last(_rendering)); + _evas_render2_wait(eo_e); +} - // 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); +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()); +} +static void +_evas_render2_updates_clean(Evas_Public_Data *e) +{ + Update *u; + + // clean out updates and tmp surfaces we were holding/tracking + EINA_LIST_FREE(e->render.updates, u) + { + //evas_cache_image_drop(u->surface); + free(u); + } +} + +static void +_evas_render2_stage_last(Eo *eo_e, Eina_Bool make_updates) +{ + Evas_Public_Data *e = eo_data_scope_get(eo_e, EVAS_CLASS); + // XXX: - // XXX: process all objects figuring out update regions + // XXX: actually update screen from mainloop here if needed - eg software + // XXX: engine needs to xshmputimage here - engine func does this // XXX: + + // if we did do rendering flush output to target and call callbacks + if (e->render.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 ((1) || (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); + // if we don't want to keep updates after this + if (!make_updates) _evas_render2_updates_clean(e); + // clean out modules we don't need anymore + evas_module_clean(); +} + +static void +_evas_render2_object_basic_process(Evas_Public_Data *e, + Evas_Object_Protected_Data *obj) +{ + printf("_evas_render2_object_basic_process %p %p\n", e, obj); +} + +static void +_evas_render2_object_process(Evas_Public_Data *e, + Evas_Object_Protected_Data *obj) +{ + // process object OR walk through child objects if smart and process those + Evas_Object_Protected_Data *obj2; + + // XXX: this needs to become parallel, BUT we need new object methods to + // call to make that possible as the current ones work on a single global + // engine handle and single orderted redraw queue. + if (obj->smart.smart) + { + EINA_INLIST_FOREACH + (evas_object_smart_members_get_direct(obj->object), obj2) + _evas_render2_object_process(e, obj2); + } + else _evas_render2_object_basic_process(e, obj); +} + +static void +_evas_render2_stage_generate_object_updates(Evas_Public_Data *e) +{ + Evas_Layer *lay; + + // XXX: should time this + EINA_INLIST_FOREACH(e->layers, lay) + { + Evas_Object_Protected_Data *obj; + EINA_INLIST_FOREACH(lay->objects, obj) + _evas_render2_object_process(e, obj); + } +} + +static void +_evas_render2_stage_explicit_updates(Evas_Public_Data *e) +{ + Eina_Rectangle *r; + Eina_List *l; + + // XXX: should time this // if the output size changed, add a full redraw if ((e->output.changed) || (e->framespace.changed)) { @@ -104,87 +248,111 @@ _evas_render2_begin(Eo *eo_e, Eina_Bool make_updates, 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: 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); - } - } +static void +_evas_render2_stage_main_render_prepare(Evas_Public_Data *e) +{ + // XXX: + // XXX: do any preparation work here that is needed for the render + // XXX: threads to do their work, but can't be done in a thread. this + // XXX: also includes doing the pose render and clear of change flag + // XXX: + printf("_evas_render2_stage_main_render_prepare %p\n", e); +} - // reset flags sinc rendering is processed now +static void +_evas_render2_stage_render_do(Evas_Public_Data *e) +{ + // XXX: + // XXX: actually render now (either in thread or in mainloop) + // XXX: + printf("_evas_render2_stage_render_do %p\n", e); +} + +static void +_evas_render2_stage_reset(Evas_Public_Data *e) +{ + // cleanup canvas state after a render e->changed = EINA_FALSE; e->viewport.changed = EINA_FALSE; e->output.changed = EINA_FALSE; e->framespace.changed = EINA_FALSE; e->invalidate = EINA_FALSE; +} +static void +_evas_render2_stage_object_cleanup(Evas_Public_Data *e) +{ + // cleanup objects no longer needed now they have been scanned // XXX: // XXX: delete objects no longer needed here // XXX: + printf("_evas_render2_stage_object_cleanup %p\n", e); +} - // if we are not going to be async then do post render here - if (!do_async) +static void +_evas_render2_th_render(void *data) +{ + Evas_Public_Data *e = data; + printf("th rend %p\n", e); + _evas_render2_stage_render_do(e); +} + +// major functions (called from evas_render.c) +////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////// +// 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); + + // 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); + // begin out actual rendering bits + _evas_render2_stage_generate_object_updates(e); + _evas_render2_stage_explicit_updates(e); + // we are actually asked to draw not just go through the motions for gc + if (do_draw) { - // 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) + // now go through any preparation that needs doing in the mainloop + _evas_render2_stage_main_render_prepare(e); + // send off rendering to primary thread renderer + if (do_async) { - Update *u; - - EINA_LIST_FREE(e->render.updates, u) - { - //evas_cache_image_drop(u->surface); - free(u); - } + // 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); + // queue the render thread command + evas_thread_cmd_enqueue(_evas_render2_th_render, e); + // flush the thread queue and call wakeup_send in the thread + evas_thread_queue_flush(_evas_render2_wakeup_send, eo_e); } + // or if not async, do rendering inline now + else _evas_render2_stage_render_do(e); } + // reset flags since rendering is processed now + _evas_render2_stage_reset(e); + // clean/delete/gc objects here + _evas_render2_stage_object_cleanup(e); + // if we are not going to be async then do last render stage here + if (!do_async) _evas_render2_stage_last(eo_e, make_updates); + if (!do_draw) _evas_render2_updates_clean(e); return EINA_TRUE; } @@ -197,50 +365,7 @@ _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(); + _evas_render2_stage_last(eo_e, EINA_TRUE); // release canvas object ref eo_unref(eo_e); } @@ -302,73 +427,3 @@ _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()); -} --
