From 40f4acae75e975041d651900cf71b20ae44ab4b6 Mon Sep 17 00:00:00 2001 From: Jean-Philippe Andre Date: Thu, 5 Jan 2017 21:29:32 +0900 Subject: [PATCH] evas: Implement buffer caching for textblock filters This is the most basic optimization that needs to be done for filters to be useful: cache the output rgba buffers for each filtered element. Hopefully this doesn't leak. I'm not making any promises about that though :) --- src/lib/evas/canvas/evas_object_textblock.c | 228 +++++++++++++------- src/lib/evas/filters/evas_filter.c | 21 +- src/lib/evas/filters/evas_filter_private.h | 2 +- src/lib/evas/include/evas_filter.h | 5 +- 4 files changed, 163 insertions(+), 93 deletions(-) diff --git a/src/lib/evas/canvas/evas_object_textblock.c b/src/lib/evas/canvas/evas_object_textblock.c index ca0ef1a00b..1cd85cd345 100644 --- a/src/lib/evas/canvas/evas_object_textblock.c +++ b/src/lib/evas/canvas/evas_object_textblock.c @@ -437,7 +437,8 @@ struct _Evas_Object_Textblock_Text_Item Evas_Object_Textblock_Item parent; /**< Textblock item. */ Evas_Text_Props text_props; /**< Props for this item. */ Evas_Coord x_adjustment; /**< Used to indicate by how much we adjusted sizes */ - int gfx_filter_id; /**< Index for the filter context in parent.format->gfx_filter */ + Evas_Filter_Context *gfx_filter_ctx; /* temp variable FIXME */ + void *gfx_buffer; /* FIXME */ }; struct _Evas_Object_Textblock_Format_Item @@ -453,25 +454,20 @@ struct _Evas_Object_Textblock_Format_Item struct _Efl_Canvas_Text_Filter { - // FIXME: sources not handled! --> global to the textblock - // FIXME: data not handled! --> global to the textblock Eina_Stringshare *name; - Eina_Array *contexts; /* contains Evas_Filter_Context items for each sub text item */ Evas_Object *eo_obj; Evas_Public_Data *evas; void *dc; /* draw context - no clip, white, no colmul... */ struct { int l, r, t, b; } pad; - int pending_ctx; Eina_Bool invalid; - Eina_Bool async; + Eina_Bool redraw; }; struct _Efl_Canvas_Text_Filter_Post_Render { - Efl_Canvas_Text_Filter *filter; - int ctx_id; + Evas_Filter_Context *ctx; Eina_Bool success; }; @@ -952,6 +948,33 @@ _format_unref_free(const Evas_Object *eo_obj, Evas_Object_Textblock_Format *fmt) free(fmt); } +typedef struct _Post_Render_Image_Unref_Job +{ + Evas_Public_Data *evas; + void *image; +} Post_Render_Image_Unref_Job; + +static void +_image_post_render_unref_cb(void *data) +{ + Post_Render_Image_Unref_Job *job = data; + + job->evas->engine.func->image_free(job->evas->engine.data.output, job->image); + free(job); +} + +static inline void +_image_safe_unref(Evas_Public_Data *e, void *image) +{ + Post_Render_Image_Unref_Job *job; + + if (!image) return; + job = calloc(1, sizeof(*job)); + job->evas = e; + job->image = image; + evas_post_render_job_add(e, _image_post_render_unref_cb, job); +} + /** * @internal * Free a layout item @@ -967,6 +990,16 @@ _item_free(const Evas_Object *eo_obj, Evas_Object_Textblock_Line *ln, Evas_Objec Evas_Object_Textblock_Text_Item *ti = _ITEM_TEXT(it); evas_common_text_props_content_unref(&ti->text_props); + if (ti->gfx_filter_ctx) + { + WRN("filter context still alive? (ignored)"); + } + if (ti->gfx_buffer) + { + Evas_Public_Data *e = efl_data_scope_get(evas_object_evas_get(eo_obj), EVAS_CANVAS_CLASS); + _image_safe_unref(e, ti->gfx_buffer); + ti->gfx_buffer = NULL; + } } else { @@ -4000,15 +4033,17 @@ _text_item_update_sizes(Ctxt *c, Evas_Object_Textblock_Text_Item *ti) Evas_Filter_Program *pgm; pgm = _format_filter_program_get(c->o, ti->parent.format); - evas_filter_program_padding_get(pgm, &l, &r, &t, &b); + if (pgm) + { + evas_filter_program_padding_get(pgm, &l, &r, &t, &b); - // FIXME: Those values are very very strange - ti->x_adjustment = r + l; - ti->parent.w = tw + ti->x_adjustment; - ti->parent.h = th; - ti->parent.adv = advw; - ti->parent.x = 0; - return; + ti->x_adjustment = r + l; + ti->parent.w = tw + ti->x_adjustment; // FIXME: why add l+r here, + ti->parent.h = th; // but not t+b here? + ti->parent.adv = advw; + ti->parent.x = 0; + return; + } } /* These adjustments are calculated and thus heavily linked to those in @@ -4590,20 +4625,17 @@ _layout_do_format(const Evas_Object *obj, Ctxt *c, } { - Evas_Coord pad_l, pad_r, pad_t, pad_b; - pad_l = pad_r = pad_t = pad_b = 0; + Evas_Coord pad_l = 0, pad_r = 0, pad_t = 0, pad_b = 0; + Evas_Filter_Program *pgm = NULL; - if (EINA_LIKELY(!fmt->gfx_filter)) - evas_text_style_pad_get(fmt->style, &pad_l, &pad_r, &pad_t, &pad_b); + if (EINA_UNLIKELY(fmt->gfx_filter != NULL)) + pgm = _format_filter_program_get(efl_data_scope_get(obj, MY_CLASS), fmt); + + if (EINA_UNLIKELY(pgm != NULL)) + evas_filter_program_padding_get(pgm, &pad_l, &pad_r, &pad_t, &pad_b); else - { - Evas_Filter_Program *pgm; - Efl_Canvas_Text_Data *o; + evas_text_style_pad_get(fmt->style, &pad_l, &pad_r, &pad_t, &pad_b); - o = efl_data_scope_get(obj, MY_CLASS); - pgm = _format_filter_program_get(o, fmt); - evas_filter_program_padding_get(pgm, &pad_l, &pad_r, &pad_t, &pad_b); - } if (pad_l > *style_pad_l) *style_pad_l = pad_l; if (pad_r > *style_pad_r) *style_pad_r = pad_r; if (pad_t > *style_pad_t) *style_pad_t = pad_t; @@ -12910,22 +12942,42 @@ evas_object_textblock_free(Evas_Object *eo_obj) free(o->utf8); } -static void -_filter_sync_end(Efl_Canvas_Text_Filter *filter, int id, Eina_Bool success) +static Evas_Filter_Context * +_filter_context_get(Evas_Object_Textblock_Text_Item *ti) { - Evas_Filter_Context *ctx; + // FIXME: optimize this (memory usage) + if (!ti) return NULL; + return ti->gfx_filter_ctx; +} - EINA_SAFETY_ON_NULL_RETURN(filter->contexts); - ctx = eina_array_data_get(filter->contexts, id); - eina_array_data_set(filter->contexts, id, NULL); - evas_filter_context_destroy(ctx); - filter->invalid = !success; +static void +_filter_context_set(Evas_Object_Textblock_Text_Item *ti, Evas_Filter_Context *ctx) +{ + if (!ti) return; + ti->gfx_filter_ctx = ctx; +} - if ((--filter->pending_ctx) == 0) +static void +_filter_sync_end(Evas_Filter_Context *ctx, Eina_Bool success) +{ + Evas_Object_Textblock_Text_Item *ti; + Efl_Canvas_Text_Filter *filter; + void *buffer; + + ti = evas_filter_context_data_get(ctx); + filter = ti->parent.format->gfx_filter; + + buffer = evas_filter_buffer_backing_steal(ctx, EVAS_FILTER_BUFFER_OUTPUT_ID); + if (buffer) { - eina_array_free(filter->contexts); - filter->contexts = NULL; + if (ti->gfx_buffer) + _image_safe_unref(filter->evas, ti->gfx_buffer); + ti->gfx_buffer = buffer; } + + evas_filter_context_destroy(ctx); + _filter_context_set(ti, NULL); + filter->invalid = !success; } static void @@ -12933,7 +12985,7 @@ _filter_post_render_cb(void *data) { Efl_Canvas_Text_Filter_Post_Render *post_data = data; - _filter_sync_end(post_data->filter, post_data->ctx_id, post_data->success); + _filter_sync_end(post_data->ctx, post_data->success); free(post_data); } @@ -12942,20 +12994,16 @@ _filter_cb(Evas_Filter_Context *ctx, void *data, Eina_Bool success) { Efl_Canvas_Text_Filter_Post_Render *post_data; Efl_Canvas_Text_Filter *filter = data; - int ctx_id; - ctx_id = evas_filter_context_id_get(ctx); - - if (!filter->async) + if (!evas_filter_context_async_get(ctx)) { - _filter_sync_end(filter, ctx_id, success); + _filter_sync_end(ctx, success); return; } post_data = calloc(1, sizeof(*post_data)); post_data->success = success; - post_data->filter = filter; - post_data->ctx_id = ctx_id; + post_data->ctx = ctx; evas_post_render_job_add(filter->evas, _filter_post_render_cb, post_data); } @@ -13098,9 +13146,8 @@ evas_object_textblock_render(Evas_Object *eo_obj EINA_UNUSED, cr = nr; cg = ng; cb = nb; ca = na; \ } -#define DRAW_TEXT_FILTER(gf, gf_id, ox, oy) do { \ - Evas_Filter_Context *ctx = eina_array_data_get(gf->contexts, gf_id); \ - evas_filter_input_render(eo_obj, ctx, gf->dc, ti, \ +#define DRAW_TEXT_FILTER(gf, ox, oy) do { \ + evas_filter_input_render(eo_obj, _filter_context_get(ti), gf->dc, ti, \ gf->pad.l, gf->pad.r, \ gf->pad.t, gf->pad.b, do_async); \ } while (0) @@ -13121,10 +13168,10 @@ evas_object_textblock_render(Evas_Object *eo_obj EINA_UNUSED, #define DRAW_TEXT(ox, oy) do { \ if (ti->parent.format->font.font) \ { \ - if (EINA_LIKELY(!ti->gfx_filter_id)) \ + if (EINA_LIKELY(!ti->gfx_filter_ctx)) \ DRAW_TEXT_NOFILTER(ox, oy); \ - else \ - DRAW_TEXT_FILTER(ti->parent.format->gfx_filter, ti->gfx_filter_id - 1, ox, oy); \ + else if (!ti->gfx_buffer) \ + DRAW_TEXT_FILTER(ti->parent.format->gfx_filter, ox, oy); \ } } while(0) /* backing */ @@ -13258,8 +13305,6 @@ evas_object_textblock_render(Evas_Object *eo_obj EINA_UNUSED, /* There are size adjustments that depend on the styles drawn here back * in "_text_item_update_sizes" should not modify one without the other. */ - /* prepare everything for text draw */ - /* gfx filters preparation */ EINA_LIST_FOREACH(gfx_filters, li, itr) { @@ -13268,27 +13313,29 @@ evas_object_textblock_render(Evas_Object *eo_obj EINA_UNUSED, Efl_Canvas_Text_Filter *filter; Evas_Filter_Program *pgm; Evas_Filter_Context *ctx; - int filter_id; Eina_Bool ok; + int X, Y; + ln = ti->parent.ln; filter = ti->parent.format->gfx_filter; pgm = _format_filter_program_get(o, ti->parent.format); if (!pgm) { WRN("Filter '%s' not found on this object", filter->name); - ti->gfx_filter_id = 0; continue; } -#ifdef DEBUG - if (filter->contexts) WRN("Previous filter context was not deleted"); -#endif + // previous run + if (ti->gfx_buffer) + { + if (!filter->redraw) continue; - if (!filter->contexts) - filter->contexts = eina_array_new(8); - filter_id = eina_array_count(filter->contexts); + _image_safe_unref(obj->layer->evas, ti->gfx_buffer); + ti->gfx_buffer = NULL; + } - ctx = evas_filter_context_new(obj->layer->evas, do_async, filter_id); + // prepare filter context + ctx = evas_filter_context_new(obj->layer->evas, do_async, ti); evas_filter_state_prepare(eo_obj, &state, ti); evas_filter_program_state_set(pgm, &state); ok = evas_filter_context_program_use(ctx, pgm); @@ -13296,26 +13343,26 @@ evas_object_textblock_render(Evas_Object *eo_obj EINA_UNUSED, { evas_filter_context_destroy(ctx); filter->invalid = EINA_TRUE; - ti->gfx_filter_id = 0; continue; } - eina_array_push(filter->contexts, ctx); - ti->gfx_filter_id = filter_id + 1; + // target position + evas_filter_program_padding_get(pgm, + &filter->pad.l, &filter->pad.r, + &filter->pad.t, &filter->pad.b); + X = obj->cur->geometry.x + ln->x + ti->parent.x + x - filter->pad.l; + Y = obj->cur->geometry.y + ln->par->y + ln->y + y - filter->pad.t; - ln = ti->parent.ln; ENFN->context_color_set(ENDT, context, 255, 255, 255, 255); ENFN->context_multiplier_set(ENDT, context, obj->cur->cache.clip.r, obj->cur->cache.clip.g, obj->cur->cache.clip.b, obj->cur->cache.clip.a); - evas_filter_program_padding_get(pgm, - &filter->pad.l, &filter->pad.r, - &filter->pad.t, &filter->pad.b); evas_filter_context_proxy_render_all(ctx, eo_obj, EINA_FALSE); evas_filter_context_buffers_allocate_all(ctx); - evas_filter_target_set(ctx, context, surface, - obj->cur->geometry.x + ln->x + ti->parent.x + x - filter->pad.l, - obj->cur->geometry.y + ln->par->y + ln->y + y - filter->pad.t); + evas_filter_target_set(ctx, context, surface, X, Y); + _filter_context_set(ti, ctx); + + // common data for all items (FIXME: should be common to object) if (!filter->dc) { filter->dc = ENFN->context_new(ENDT); @@ -13323,8 +13370,6 @@ evas_object_textblock_render(Evas_Object *eo_obj EINA_UNUSED, } filter->eo_obj = eo_obj; filter->evas = obj->layer->evas; - filter->async = do_async; - filter->pending_ctx++; ENFN->context_multiplier_unset(ENDT, context); } @@ -13342,7 +13387,7 @@ evas_object_textblock_render(Evas_Object *eo_obj EINA_UNUSED, yoff = itr->yoff; ln = itr->ln; - if (EINA_UNLIKELY(ti->gfx_filter_id)) + if (EINA_UNLIKELY(ti->gfx_filter_ctx != NULL)) context = ti->parent.format->gfx_filter->dc; shad_dst = shad_sz = dx = dy = haveshad = 0; @@ -13457,7 +13502,7 @@ evas_object_textblock_render(Evas_Object *eo_obj EINA_UNUSED, yoff = itr->yoff; ln = itr->ln; - if (EINA_UNLIKELY(ti->gfx_filter_id)) + if (EINA_UNLIKELY(ti->gfx_filter_ctx != NULL)) context = ti->parent.format->gfx_filter->dc; if ((ti->parent.format->style & EVAS_TEXT_STYLE_MASK_BASIC) == EVAS_TEXT_STYLE_GLOW) @@ -13495,7 +13540,7 @@ evas_object_textblock_render(Evas_Object *eo_obj EINA_UNUSED, yoff = itr->yoff; ln = itr->ln; - if (EINA_UNLIKELY(ti->gfx_filter_id)) + if (EINA_UNLIKELY(ti->gfx_filter_ctx != NULL)) context = ti->parent.format->gfx_filter->dc; if (((ti->parent.format->style & EVAS_TEXT_STYLE_MASK_BASIC) == EVAS_TEXT_STYLE_OUTLINE) || @@ -13580,7 +13625,7 @@ evas_object_textblock_render(Evas_Object *eo_obj EINA_UNUSED, { void *fi = _ITEM_TEXT(itr)->text_props.font_instance; - if (EINA_UNLIKELY(ti->gfx_filter_id)) + if (EINA_UNLIKELY(ti->gfx_filter_ctx != NULL)) context = ti->parent.format->gfx_filter->dc; COLOR_SET(normal); @@ -13590,13 +13635,30 @@ evas_object_textblock_render(Evas_Object *eo_obj EINA_UNUSED, context = context_save; - if (EINA_UNLIKELY(ti->gfx_filter_id)) + if (EINA_UNLIKELY(ti->parent.format->gfx_filter != NULL)) { + Evas_Filter_Context *ctx = _filter_context_get(ti); Efl_Canvas_Text_Filter *filter = ti->parent.format->gfx_filter; - Evas_Filter_Context *ctx = eina_array_data_get(filter->contexts, ti->gfx_filter_id - 1); + void *buffer = ti->gfx_buffer; - evas_filter_context_post_run_callback_set(ctx, _filter_cb, filter); - evas_filter_run(ctx); + if (buffer) + { + int X, Y, W = 0, H = 0; + + X = obj->cur->geometry.x + ln->x + ti->parent.x + x - filter->pad.l; + Y = obj->cur->geometry.y + ln->par->y + ln->y + y - filter->pad.t; + + ENFN->context_color_set(ENDT, context, 255, 255, 255, 255); + ENFN->context_multiplier_unset(ENDT, context); + ENFN->image_size_get(ENDT, buffer, &W, &H); + ENFN->image_draw(ENDT, context, surface, buffer, + 0, 0, W, H, X, Y, W, H, 0, do_async); + } + else if (ctx) + { + evas_filter_context_post_run_callback_set(ctx, _filter_cb, filter); + evas_filter_run(ctx); + } } } diff --git a/src/lib/evas/filters/evas_filter.c b/src/lib/evas/filters/evas_filter.c index 2b6fbae23c..6ce9cb980d 100644 --- a/src/lib/evas/filters/evas_filter.c +++ b/src/lib/evas/filters/evas_filter.c @@ -49,7 +49,7 @@ _evas_image_get(Ector_Buffer *buf) /* Main functions */ Evas_Filter_Context * -evas_filter_context_new(Evas_Public_Data *evas, Eina_Bool async, int id) +evas_filter_context_new(Evas_Public_Data *evas, Eina_Bool async, void *user_data) { Evas_Filter_Context *ctx; @@ -60,17 +60,23 @@ evas_filter_context_new(Evas_Public_Data *evas, Eina_Bool async, int id) ctx->evas = evas; ctx->async = async; - ctx->context_id = id; + ctx->user_data = user_data; return ctx; } -int -evas_filter_context_id_get(Evas_Filter_Context *ctx) +void * +evas_filter_context_data_get(Evas_Filter_Context *ctx) { - EINA_SAFETY_ON_NULL_RETURN_VAL(ctx, -1); + EINA_SAFETY_ON_NULL_RETURN_VAL(ctx, NULL); - return ctx->context_id; + return ctx->user_data; +} + +Eina_Bool +evas_filter_context_async_get(Evas_Filter_Context *ctx) +{ + return ctx->async; } /* Private function to reset the filter context. Used from parser.c */ @@ -1352,6 +1358,8 @@ evas_filter_font_draw(Evas_Filter_Context *ctx, void *draw_context, int bufid, void *surface = NULL; fb = _filter_buffer_get(ctx, bufid); + EINA_SAFETY_ON_NULL_RETURN_VAL(fb, EINA_FALSE); + surface = _evas_image_get(fb->buffer); if (!surface) return EINA_FALSE; @@ -1364,7 +1372,6 @@ evas_filter_font_draw(Evas_Filter_Context *ctx, void *draw_context, int bufid, evas_common_font_glyphs_ref(text_props->glyphs); evas_unref_queue_glyph_put(ctx->evas, text_props->glyphs); } - //evas_common_save_image_to_file(surface, "/tmp/input.png", 0, 100, 0 ,0); return EINA_TRUE; } diff --git a/src/lib/evas/filters/evas_filter_private.h b/src/lib/evas/filters/evas_filter_private.h index 4c54c9615a..0eb2052b0c 100644 --- a/src/lib/evas/filters/evas_filter_private.h +++ b/src/lib/evas/filters/evas_filter_private.h @@ -116,7 +116,7 @@ struct _Evas_Filter_Context Eina_List *buffers; // Evas_Filter_Buffer * int last_buffer_id; int last_command_id; - int context_id; // used by textblock + void *user_data; // used by textblock // Variables changing at each run int w, h; // Dimensions of the input/output buffers diff --git a/src/lib/evas/include/evas_filter.h b/src/lib/evas/include/evas_filter.h index 0ee3f30e9d..c8962c434d 100644 --- a/src/lib/evas/include/evas_filter.h +++ b/src/lib/evas/include/evas_filter.h @@ -137,8 +137,9 @@ EAPI void evas_filter_program_source_set_all(Evas_Filter_Program void evas_filter_program_data_set_all(Evas_Filter_Program *pgm, Eina_Inlist *data); /* Filter context (low level) */ -Evas_Filter_Context *evas_filter_context_new(Evas_Public_Data *evas, Eina_Bool async, int id); -int evas_filter_context_id_get(Evas_Filter_Context *ctx); +Evas_Filter_Context *evas_filter_context_new(Evas_Public_Data *evas, Eina_Bool async, void *user_data); +void *evas_filter_context_data_get(Evas_Filter_Context *ctx); +Eina_Bool evas_filter_context_async_get(Evas_Filter_Context *ctx); void evas_filter_context_destroy(Evas_Filter_Context *ctx); Eina_Bool evas_filter_context_program_use(Evas_Filter_Context *ctx, Evas_Filter_Program *pgm); void evas_filter_context_proxy_render_all(Evas_Filter_Context *ctx, Eo *eo_obj, Eina_Bool do_async);