From b0530aba4f777352cc3ae9772fb1d22f598679a5 Mon Sep 17 00:00:00 2001 From: "Carsten Haitzler (Rasterman)" Date: Fri, 30 Dec 2016 18:55:55 +0900 Subject: [PATCH] evas cutouts - quickly avoid huge per issues with large nos of cutouts i found evas_common_draw_context_apply_cutouts() was procsessing 300+ cutouts and as it's O(n^2)/2 to try and merge adjacent rects for cutouts this really performs like complete junk. we apply cutout rects a LOT. this is not the best solution, but it's quick and much faster than doing the clipouts which drop framerate to like 1-2fps or so in the nasty case i say (tyls -m of photos in a dir with a 2160 high terminal). this figures out the target area to limit the count of rects significantly so O(n^2) is far far better when n is now < 10 most of the time. and for the few operations where it's a high value this now uses qsort to speed up merges etc. etc. @optimize --- src/lib/evas/canvas/evas_object_text.c | 33 ++-- src/lib/evas/canvas/evas_object_textblock.c | 20 +- src/lib/evas/canvas/evas_object_textgrid.c | 12 ++ src/lib/evas/canvas/evas_render.c | 5 + src/lib/evas/common/evas_draw.h | 2 +- src/lib/evas/common/evas_draw_main.c | 186 +++++++++++++----- src/lib/evas/include/evas_common_private.h | 3 + src/lib/evas/include/evas_private.h | 1 + .../engines/software_generic/evas_engine.c | 8 + 9 files changed, 200 insertions(+), 70 deletions(-) diff --git a/src/lib/evas/canvas/evas_object_text.c b/src/lib/evas/canvas/evas_object_text.c index 4d302b1233..a4f8702c8b 100644 --- a/src/lib/evas/canvas/evas_object_text.c +++ b/src/lib/evas/canvas/evas_object_text.c @@ -1805,21 +1805,26 @@ evas_object_text_render(Evas_Object *eo_obj, (((int)object->sub.col.b) * (amul)) / 255, \ (((int)object->sub.col.a) * (amul)) / 255); -#define DRAW_TEXT(ox, oy) \ - if ((o->font) && (it->text_props.len > 0)) \ - evas_font_draw_async_check(obj, output, \ - context, \ - surface, \ - o->font, \ +#define DRAW_TEXT(ox, oy) \ + if ((o->font) && (it->text_props.len > 0)) { \ + ENFN->context_cutout_target(output, context, \ + obj->cur->geometry.x + x + sl + ox + it->x, \ + obj->cur->geometry.y + y + st + oy, \ + it->w, it->h); \ + evas_font_draw_async_check(obj, output, \ + context, \ + surface, \ + o->font, \ obj->cur->geometry.x + x + sl + ox + it->x, \ - obj->cur->geometry.y + y + st + oy + \ - (int) o->max_ascent, \ - obj->cur->geometry.w, \ - obj->cur->geometry.h, \ - obj->cur->geometry.w, \ - obj->cur->geometry.h, \ - &it->text_props, \ - do_async); + obj->cur->geometry.y + y + st + oy + \ + (int)o->max_ascent, \ + obj->cur->geometry.w, \ + obj->cur->geometry.h, \ + obj->cur->geometry.w, \ + obj->cur->geometry.h, \ + &it->text_props, \ + do_async); \ + } if (o->has_filter) { diff --git a/src/lib/evas/canvas/evas_object_textblock.c b/src/lib/evas/canvas/evas_object_textblock.c index 5115bb0719..638c5d56df 100644 --- a/src/lib/evas/canvas/evas_object_textblock.c +++ b/src/lib/evas/canvas/evas_object_textblock.c @@ -12862,13 +12862,18 @@ evas_object_textblock_render(Evas_Object *eo_obj EINA_UNUSED, cr = nr; cg = ng; cb = nb; ca = na; \ } #define DRAW_TEXT(ox, oy) \ - if (ti->parent.format->font.font) \ - evas_font_draw_async_check(obj, output, context, surface, \ + if (ti->parent.format->font.font) { \ + ENFN->context_cutout_target(output, context, \ + obj->cur->geometry.x + ln->x - (ln->h * 4) + ti->parent.x + x + (ox) - 100, \ + obj->cur->geometry.y + ln->par->y + ln->y - ln->h + y + (oy), \ + ti->parent.w + (ln->h * 8), ln->h * 3); \ + evas_font_draw_async_check(obj, output, context, surface, \ ti->parent.format->font.font, \ - obj->cur->geometry.x + ln->x + ti->parent.x + x + (ox), \ - obj->cur->geometry.y + ln->par->y + ln->y + yoff + y + (oy), \ + obj->cur->geometry.x + ln->x + ti->parent.x + x + (ox), \ + obj->cur->geometry.y + ln->par->y + ln->y + yoff + y + (oy), \ ti->parent.w, ti->parent.h, ti->parent.w, ti->parent.h, \ - &ti->text_props, do_async); + &ti->text_props, do_async); \ + } /* backing */ #define DRAW_RECT(ox, oy, ow, oh, or, og, ob, oa) \ @@ -12884,6 +12889,11 @@ evas_object_textblock_render(Evas_Object *eo_obj EINA_UNUSED, nr / 255, ng / 255, nb / 255, na / 255); \ cr = nr; cg = ng; cb = nb; ca = na; \ } \ + ENFN->context_cutout_target(output, context, \ + obj->cur->geometry.x + ln->x + x + (ox), \ + obj->cur->geometry.y + ln->par->y + ln->y + y + (oy), \ + (ow), \ + (oh)); \ ENFN->rectangle_draw(output, \ context, \ surface, \ diff --git a/src/lib/evas/canvas/evas_object_textgrid.c b/src/lib/evas/canvas/evas_object_textgrid.c index 1c19a0188c..390b716c00 100644 --- a/src/lib/evas/canvas/evas_object_textgrid.c +++ b/src/lib/evas/canvas/evas_object_textgrid.c @@ -531,6 +531,9 @@ evas_object_textgrid_render(Evas_Object *eo_obj EINA_UNUSED, ENFN->context_color_set(output, context, row->rects[xx].r, row->rects[xx].g, row->rects[xx].b, row->rects[xx].a); + ENFN->context_cutout_target(output, context, + xp + row->rects[xx].x, yp, + row->rects[xx].w, h); ENFN->rectangle_draw(output, context, surface, xp + row->rects[xx].x, yp, row->rects[xx].w, h, @@ -600,6 +603,9 @@ evas_object_textgrid_render(Evas_Object *eo_obj EINA_UNUSED, } while (font == current_font); + ENFN->context_cutout_target(output, context, + xp - w, yp + o->ascent - h, + w * 3, h * 3); async_unref = ENFN->multi_font_draw(output, context, surface, current_font, @@ -639,6 +645,9 @@ evas_object_textgrid_render(Evas_Object *eo_obj EINA_UNUSED, ENFN->context_color_set(output, context, r, g, b, a); font = _textgrid_font_get(o, text->bold, text->italic); + ENFN->context_cutout_target(output, context, + tx - w, ty - h, + w * 3, h * 3); evas_font_draw_async_check(obj, output, context, surface, font, tx, ty, ww, hh, ww, hh, props, do_async); @@ -651,6 +660,9 @@ evas_object_textgrid_render(Evas_Object *eo_obj EINA_UNUSED, ENFN->context_color_set(output, context, row->lines[xx].r, row->lines[xx].g, row->lines[xx].b, row->lines[xx].a); + ENFN->context_cutout_target(output, context, + xp + row->lines[xx].x, yp + row->lines[xx].y, + row->lines[xx].w, 1); ENFN->rectangle_draw(output, context, surface, xp + row->lines[xx].x, yp + row->lines[xx].y, row->lines[xx].w, 1, diff --git a/src/lib/evas/canvas/evas_render.c b/src/lib/evas/canvas/evas_render.c index bf4bf130a5..252f0b6350 100644 --- a/src/lib/evas/canvas/evas_render.c +++ b/src/lib/evas/canvas/evas_render.c @@ -2878,6 +2878,11 @@ evas_render_updates_internal_loop(Evas *eo_e, Evas_Public_Data *evas, _evas_render_cutout_add(evas, context, obj2, off_x + fx, off_y + fy); } #endif + ENFN->context_cutout_target(ENDT, context, + off_x + obj->cur->cache.clip.x, + off_y + obj->cur->cache.clip.y, + obj->cur->cache.clip.w, + obj->cur->cache.clip.h); eina_evlog("-cutouts_add", obj->object, 0.0, NULL); clean_them |= evas_render_mapped(evas, eo_obj, obj, context, surface, off_x + fx, diff --git a/src/lib/evas/common/evas_draw.h b/src/lib/evas/common/evas_draw.h index 0623ea4662..374b2c799a 100644 --- a/src/lib/evas/common/evas_draw.h +++ b/src/lib/evas/common/evas_draw.h @@ -34,6 +34,6 @@ EAPI void evas_common_draw_context_set_anti_alias (RGBA_D EAPI void evas_common_draw_context_set_color_interpolation (RGBA_Draw_Context *dc, int color_space); EAPI void evas_common_draw_context_set_render_op (RGBA_Draw_Context *dc, int op); EAPI void evas_common_draw_context_set_sli (RGBA_Draw_Context *dc, int y, int h); - +EAPI void evas_common_draw_context_target_set (RGBA_Draw_Context *dc, int x, int y, int w, int h); #endif /* _EVAS_DRAW_H */ diff --git a/src/lib/evas/common/evas_draw_main.c b/src/lib/evas/common/evas_draw_main.c index b87c473bd6..60ab694e90 100644 --- a/src/lib/evas/common/evas_draw_main.c +++ b/src/lib/evas/common/evas_draw_main.c @@ -590,39 +590,66 @@ evas_common_draw_context_cutout_split(Cutout_Rects *res, int idx, Cutout_Rect *s #undef R_NEW } +EAPI void +evas_common_draw_context_target_set(RGBA_Draw_Context *dc, int x, int y, int w, int h) +{ + dc->cutout_target.x = x; + dc->cutout_target.y = y; + dc->cutout_target.w = w; + dc->cutout_target.h = h; +} + +static int +_srt_y(const void *d1, const void *d2) +{ + const Cutout_Rect *r1 = d1, *r2 = d2; + if (r1->y == r2->y) return r1->x - r2->x; + return r1->y - r2->y; +} + +static int +_srt_x(const void *d1, const void *d2) +{ + const Cutout_Rect *r1 = d1, *r2 = d2; + if (r1->x == r2->x) return r1->y - r2->y; + return r1->x - r2->x; +} + EAPI Cutout_Rects * evas_common_draw_context_apply_cutouts(RGBA_Draw_Context *dc, Cutout_Rects *reuse) { Cutout_Rects *res = NULL; - int i; - int j; + int i, j, active, found = 0; if (!dc->clip.use) return NULL; if ((dc->clip.w <= 0) || (dc->clip.h <= 0)) return NULL; - - if (!reuse) - { - res = evas_common_draw_context_cutouts_new(); - } + if (!reuse) res = evas_common_draw_context_cutouts_new(); else { evas_common_draw_context_cutouts_free(reuse); res = reuse; } + // this avoids a nasty case of O(n^2)/2 below with lots of rectangles + // to merge so only do this merging if the number of rects is small enough + // not to blow out into insanity evas_common_draw_context_cutouts_add(res, dc->clip.x, dc->clip.y, dc->clip.w, dc->clip.h); - - for (i = 0; i < dc->cutout.active; ++i) + for (i = 0; i < dc->cutout.active; i++) { - /* Don't loop on the element just added to the list as they are already correctly clipped. */ - int active = res->active; - + if ((dc->cutout_target.w != 0) && + (!RECTS_INTERSECT(dc->cutout.rects[i].x, dc->cutout.rects[i].y, + dc->cutout.rects[i].w, dc->cutout.rects[i].h, + dc->cutout_target.x, dc->cutout_target.y, + dc->cutout_target.w, dc->cutout_target.h))) + continue; + // Don't loop on the element just added to the list as they are + // already correctly clipped. + active = res->active; for (j = 0; j < active; ) { - if (evas_common_draw_context_cutout_split(res, j, dc->cutout.rects + i)) - ++j; - else - active--; + if (evas_common_draw_context_cutout_split + (res, j, dc->cutout.rects + i)) j++; + else active--; } } /* merge rects */ @@ -630,66 +657,125 @@ evas_common_draw_context_apply_cutouts(RGBA_Draw_Context *dc, Cutout_Rects *reus #define RJ res->rects[j] if (res->active > 1) { - int found = 1; - - while (found) + if (res->active > 5) { - found = 0; + // fast path for larger numbers of rects to merge by using + // qsort to sort by y and x to limit the number of rects + // we have to walk as rects that have a different y cannot + // be merged anyway (or x). + qsort(res->rects, res->active, sizeof(res->rects[0]), _srt_y); for (i = 0; i < res->active; i++) { + if (RI.w == 0) continue; // skip empty rect for (j = i + 1; j < res->active; j++) { - /* skip empty rects we are removing */ - if (RJ.w == 0) continue; - /* check if its same width, immediately above or below */ - if ((RJ.w == RI.w) && (RJ.x == RI.x)) + if (RJ.y != RI.y) break; // new line, sorted thus skip + if (RJ.w == 0) continue; // skip empty rect + // if J is the same height (could be merged) + if (RJ.h == RI.h) { - if ((RJ.y + RJ.h) == RI.y) /* above */ + // if J is immediately to the right of I + if (RJ.x == (RI.x + RI.w)) { - RI.y = RJ.y; - RI.h += RJ.h; - RJ.w = 0; - found = 1; - } - else if ((RI.y + RI.h) == RJ.y) /* below */ - { - RI.h += RJ.h; - RJ.w = 0; - found = 1; + RI.w = (RJ.x + RJ.w) - RI.x; // expand RI + RJ.w = 0; // invalidate + found++; } + // since we sort y and THEN x, if height matches + // but it's not immediately adjacent, no more + // rects exists that can be merged + else break; } - /* check if its same height, immediately left or right */ - else if ((RJ.h == RI.h) && (RJ.y == RI.y)) + } + } + qsort(res->rects, res->active, sizeof(res->rects[0]), _srt_x); + for (i = 0; i < res->active; i++) + { + if (RI.w == 0) continue; // skip empty rect + for (j = i + 1; j < res->active; j++) + { + if (RJ.x != RI.x) break; // new line, sorted thus skip + if (RJ.w == 0) continue; // skip empty rect + // if J is the same height (could be merged) + if (RJ.w == RI.w) { - if ((RJ.x + RJ.w) == RI.x) /* left */ + // if J is immediately to the right of I + if (RJ.y == (RI.y + RI.h)) { - RI.x = RJ.x; - RI.w += RJ.w; - RJ.w = 0; - found = 1; + RI.h = (RJ.y + RJ.h) - RI.y; // expand RI + RJ.w = 0; // invalidate + found++; } - else if ((RI.x + RI.w) == RJ.x) /* right */ + // since we sort y and THEN x, if height matches + // but it's not immediately adjacent, no more + // rects exists that can be merged + else break; + } + } + } + } + else + { + // for a small number of rects, keep things simple as the count + // is small and big-o complexity isnt a problem yet + found = 1; + while (found) + { + found = 0; + for (i = 0; i < res->active; i++) + { + for (j = i + 1; j < res->active; j++) + { + // skip empty rects we are removing + if (RJ.w == 0) continue; + // check if its same width, immediately above or below + if ((RJ.w == RI.w) && (RJ.x == RI.x)) { - RI.w += RJ.w; - RJ.w = 0; - found = 1; + if ((RJ.y + RJ.h) == RI.y) // above + { + RI.y = RJ.y; + RI.h += RJ.h; + RJ.w = 0; + found++; + } + else if ((RI.y + RI.h) == RJ.y) // below + { + RI.h += RJ.h; + RJ.w = 0; + found++; + } + } + // check if its same height, immediately left or right + else if ((RJ.h == RI.h) && (RJ.y == RI.y)) + { + if ((RJ.x + RJ.w) == RI.x) // left + { + RI.x = RJ.x; + RI.w += RJ.w; + RJ.w = 0; + found++; + } + else if ((RI.x + RI.w) == RJ.x) // right + { + RI.w += RJ.w; + RJ.w = 0; + found++; + } } } } } } - /* Repack the cutout */ + // Repack the cutout j = 0; for (i = 0; i < res->active; i++) { if (RI.w == 0) continue; - if (i != j) - RJ = RI; + if (i != j) RJ = RI; j++; } res->active = j; - return res; } return res; } diff --git a/src/lib/evas/include/evas_common_private.h b/src/lib/evas/include/evas_common_private.h index 9311e2ce7d..1fa8ae3293 100644 --- a/src/lib/evas/include/evas_common_private.h +++ b/src/lib/evas/include/evas_common_private.h @@ -756,6 +756,9 @@ struct _RGBA_Draw_Context Eina_Bool async : 1; } clip; Cutout_Rects cutout; + struct { + int x, y, w, h; + } cutout_target; struct { Cutout_Rects *rects; int used; diff --git a/src/lib/evas/include/evas_private.h b/src/lib/evas/include/evas_private.h index 7221306aae..b75ef49613 100644 --- a/src/lib/evas/include/evas_private.h +++ b/src/lib/evas/include/evas_private.h @@ -1375,6 +1375,7 @@ struct _Evas_Func int (*context_multiplier_get) (void *data, void *context, int *r, int *g, int *b, int *a); void (*context_cutout_add) (void *data, void *context, int x, int y, int w, int h); void (*context_cutout_clear) (void *data, void *context); + void (*context_cutout_target) (void *data, void *context, int x, int y, int w, int h); void (*context_anti_alias_set) (void *data, void *context, unsigned char aa); unsigned char (*context_anti_alias_get) (void *data, void *context); void (*context_color_interpolation_set) (void *data, void *context, int color_space); diff --git a/src/modules/evas/engines/software_generic/evas_engine.c b/src/modules/evas/engines/software_generic/evas_engine.c index 20d1569c31..039459c9a2 100644 --- a/src/modules/evas/engines/software_generic/evas_engine.c +++ b/src/modules/evas/engines/software_generic/evas_engine.c @@ -645,9 +645,16 @@ eng_context_cutout_add(void *data EINA_UNUSED, void *context, int x, int y, int static void eng_context_cutout_clear(void *data EINA_UNUSED, void *context) { + evas_common_draw_context_target_set(context, 0, 0, 0, 0); evas_common_draw_context_clear_cutouts(context); } +static void +eng_context_cutout_target(void *data EINA_UNUSED, void *context, int x, int y, int w, int h) +{ + evas_common_draw_context_target_set(context, x, y, w, h); +} + static void eng_context_anti_alias_set(void *data EINA_UNUSED, void *context, unsigned char aa) { @@ -4655,6 +4662,7 @@ static Evas_Func func = eng_context_multiplier_get, eng_context_cutout_add, eng_context_cutout_clear, + eng_context_cutout_target, eng_context_anti_alias_set, eng_context_anti_alias_get, eng_context_color_interpolation_set,