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
This commit is contained in:
Carsten Haitzler 2016-12-30 18:55:55 +09:00
parent 5a9c6d393a
commit b0530aba4f
9 changed files with 200 additions and 70 deletions

View File

@ -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)
{

View File

@ -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, \

View File

@ -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,

View File

@ -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,

View File

@ -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 */

View File

@ -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;
}

View File

@ -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;

View File

@ -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);

View File

@ -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,