raster pushed a commit to branch master.

http://git.enlightenment.org/core/efl.git/commit/?id=b0530aba4f777352cc3ae9772fb1d22f598679a5

commit b0530aba4f777352cc3ae9772fb1d22f598679a5
Author: Carsten Haitzler (Rasterman) <ras...@rasterman.com>
Date:   Fri Dec 30 18:55:55 2016 +0900

    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               | 182 +++++++++++++++------
 src/lib/evas/include/evas_common_private.h         |   3 +
 src/lib/evas/include/evas_private.h                |   1 +
 .../evas/engines/software_generic/evas_engine.c    |   8 +
 9 files changed, 198 insertions(+), 68 deletions(-)

diff --git a/src/lib/evas/canvas/evas_object_text.c 
b/src/lib/evas/canvas/evas_object_text.c
index 4d302b1..a4f8702 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 5115bb0..638c5d5 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 1c19a01..390b716 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 bf4bf13..252f0b6 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 0623ea4..374b2c7 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 b87c473..60ab694 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;
+                                 RI.w = (RJ.x + RJ.w) - RI.x; // expand RI
+                                 RJ.w = 0; // invalidate
+                                 found++;
                               }
-                            else if ((RI.y + RI.h) == RJ.y) /* below */
+                            // 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.h;
-                                 RJ.w = 0;
-                                 found = 1;
+                                 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;
                          }
-                       /* check if its same height, immediately left or right 
*/
-                       else if ((RJ.h == RI.h) && (RJ.y == RI.y))
+                    }
+               }
+          }
+        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++)
                          {
-                            if ((RJ.x + RJ.w) == RI.x) /* left */
+                            // 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.x = RJ.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++;
+                                   }
                               }
-                            else if ((RI.x + RI.w) == RJ.x) /* right */
+                            // check if its same height, immediately left or 
right
+                            else if ((RJ.h == RI.h) && (RJ.y == RI.y))
                               {
-                                 RI.w += RJ.w;
-                                 RJ.w = 0;
-                                 found = 1;
+                                 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 9311e2c..1fa8ae3 100644
--- a/src/lib/evas/include/evas_common_private.h
+++ b/src/lib/evas/include/evas_common_private.h
@@ -757,6 +757,9 @@ struct _RGBA_Draw_Context
    } clip;
    Cutout_Rects cutout;
    struct {
+      int x, y, w, h;
+   } cutout_target;
+   struct {
       Cutout_Rects *rects;
       int used;
    } cache;
diff --git a/src/lib/evas/include/evas_private.h 
b/src/lib/evas/include/evas_private.h
index 7221306..b75ef49 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 20d1569..039459c 100644
--- a/src/modules/evas/engines/software_generic/evas_engine.c
+++ b/src/modules/evas/engines/software_generic/evas_engine.c
@@ -645,10 +645,17 @@ 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)
 {
    evas_common_draw_context_set_anti_alias(context, 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,

-- 


Reply via email to