forked from enlightenment/efl
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:
parent
5a9c6d393a
commit
b0530aba4f
|
@ -1806,20 +1806,25 @@ evas_object_text_render(Evas_Object *eo_obj,
|
|||
(((int)object->sub.col.a) * (amul)) / 255);
|
||||
|
||||
#define DRAW_TEXT(ox, oy) \
|
||||
if ((o->font) && (it->text_props.len > 0)) \
|
||||
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, \
|
||||
(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);
|
||||
do_async); \
|
||||
}
|
||||
|
||||
if (o->has_filter)
|
||||
{
|
||||
|
|
|
@ -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) \
|
||||
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), \
|
||||
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, \
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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,8 +657,68 @@ 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;
|
||||
|
||||
if (res->active > 5)
|
||||
{
|
||||
// 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++)
|
||||
{
|
||||
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 J is immediately to the right of I
|
||||
if (RJ.x == (RI.x + RI.w))
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
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 J is immediately to the right of I
|
||||
if (RJ.y == (RI.y + RI.h))
|
||||
{
|
||||
RI.h = (RJ.y + RJ.h) - RI.y; // 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
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;
|
||||
|
@ -639,57 +726,56 @@ evas_common_draw_context_apply_cutouts(RGBA_Draw_Context *dc, Cutout_Rects *reus
|
|||
{
|
||||
for (j = i + 1; j < res->active; j++)
|
||||
{
|
||||
/* skip empty rects we are removing */
|
||||
// skip empty rects we are removing
|
||||
if (RJ.w == 0) continue;
|
||||
/* check if its same width, immediately above or below */
|
||||
// check if its same width, immediately above or below
|
||||
if ((RJ.w == RI.w) && (RJ.x == RI.x))
|
||||
{
|
||||
if ((RJ.y + RJ.h) == RI.y) /* above */
|
||||
if ((RJ.y + RJ.h) == RI.y) // above
|
||||
{
|
||||
RI.y = RJ.y;
|
||||
RI.h += RJ.h;
|
||||
RJ.w = 0;
|
||||
found = 1;
|
||||
found++;
|
||||
}
|
||||
else if ((RI.y + RI.h) == RJ.y) /* below */
|
||||
else if ((RI.y + RI.h) == RJ.y) // below
|
||||
{
|
||||
RI.h += RJ.h;
|
||||
RJ.w = 0;
|
||||
found = 1;
|
||||
found++;
|
||||
}
|
||||
}
|
||||
/* check if its same height, immediately left or right */
|
||||
// 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 */
|
||||
if ((RJ.x + RJ.w) == RI.x) // left
|
||||
{
|
||||
RI.x = RJ.x;
|
||||
RI.w += RJ.w;
|
||||
RJ.w = 0;
|
||||
found = 1;
|
||||
found++;
|
||||
}
|
||||
else if ((RI.x + RI.w) == RJ.x) /* right */
|
||||
else if ((RI.x + RI.w) == RJ.x) // right
|
||||
{
|
||||
RI.w += RJ.w;
|
||||
RJ.w = 0;
|
||||
found = 1;
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Reference in New Issue