evas vg: improve surface caching mechanism. 60/192660/1
authorHermet Park <hermetpark@gmail.com>
Wed, 7 Nov 2018 10:29:05 +0000 (19:29 +0900)
committerHermet Park <hermetpark@gmail.com>
Wed, 7 Nov 2018 10:37:40 +0000 (19:37 +0900)
As we inspected, Caching surfaces for each animation frames
is not effective most times. Those animation frames
are volatible, unlikely to be used again.

They just make surface cache-pool fill trashes full.

So here improvement is not to cache every frames for animatable vector
object but do only start and end frame surfaces.

Change-Id: I5d0e21cda40ba0a9dcdd6bcd9a1cfe702fca60ed

src/lib/evas/canvas/efl_canvas_vg.c
src/lib/evas/canvas/evas_vg_private.h
src/lib/evas/common/evas_common_generic_cache.c
src/lib/evas/vg/evas_vg_cache.c

index ce5c153..71df7c9 100644 (file)
@@ -115,11 +115,24 @@ _evas_vg_resize(void *data, const Efl_Event *ev)
 }
 
 EOLIAN static Efl_VG *
-_efl_canvas_vg_root_node_get(const Eo *obj EINA_UNUSED, Efl_Canvas_Vg_Data *pd)
+_efl_canvas_vg_root_node_get(const Eo *obj, Efl_Canvas_Vg_Data *pd)
 {
    Efl_VG *root;
 
-   if (pd->vg_entry) root = evas_cache_vg_tree_get(pd->vg_entry, pd->frame_index);
+   if (pd->vg_entry)
+     {
+        Evas_Coord w, h;
+        evas_object_geometry_get(obj, NULL, NULL, &w, &h);
+
+        //Update vg data with current size.
+        if ((pd->vg_entry->w != w) || (pd->vg_entry->h != h))
+          {
+             Vg_Cache_Entry *vg_entry = evas_cache_vg_entry_resize(pd->vg_entry, w, h);
+             evas_cache_vg_entry_del(pd->vg_entry);
+             pd->vg_entry = vg_entry;
+          }
+        root = evas_cache_vg_tree_get(pd->vg_entry, pd->frame_index);
+     }
    else if (pd->user_entry) root = pd->user_entry->root;
    else root = pd->root;
 
@@ -169,14 +182,13 @@ _efl_canvas_vg_root_node_set(Eo *obj, Efl_Canvas_Vg_Data *pd, Efl_VG *root_node)
         // drop any surface cache attached to it.
         Evas_Object_Protected_Data *eobj = efl_data_scope_get(obj, EFL_CANVAS_OBJECT_CLASS);
         eobj->layer->evas->engine.func->ector_surface_cache_drop(_evas_engine_context(eobj->layer->evas),
-                                                                 pd->cache_key);
+                                                                 pd->user_entry->root);
         free(pd->user_entry);
         pd->user_entry = NULL;
      }
 
    // force a redraw
    pd->changed = EINA_TRUE;
-   pd->cache_key = NULL;
    evas_object_change(obj, efl_data_scope_get(obj, EFL_CANVAS_OBJECT_CLASS));
 }
 
@@ -264,7 +276,6 @@ _vg_file_mmap_set(Eo *eo_obj, Efl_Canvas_Vg_Data *pd, const Eina_File *file, con
    else
      pd->vg_entry = NULL;
 
-   pd->cache_key = NULL;
    evas_object_change(eo_obj, obj);
    evas_cache_vg_entry_del(old_entry);
 
@@ -503,8 +514,7 @@ _evas_vg_render(Evas_Object_Protected_Data *obj, Efl_Canvas_Vg_Data *pd,
      }
 }
 
-// renders a vg_tree to an offscreen buffer
-// and push it to the cache.
+//renders a vg_tree to an offscreen buffer and push it to the cache.
 static void *
 _render_to_buffer(Evas_Object_Protected_Data *obj, Efl_Canvas_Vg_Data *pd,
                   void *engine, void *surface,
@@ -519,20 +529,19 @@ _render_to_buffer(Evas_Object_Protected_Data *obj, Efl_Canvas_Vg_Data *pd,
    ector = evas_ector_get(obj->layer->evas);
    if (!ector) return NULL;
 
+   //create a buffer
    if (!buffer)
      {
-        // 2. create a buffer
         buffer = obj->layer->evas->engine.func->ector_surface_create(engine,
                                                                      w, h,
                                                                      &error);
-        if (error) return NULL; // surface creation error
+        if (error) return NULL;
         buffer_created = EINA_TRUE;
      }
 
-   //1. render pre
    _evas_vg_render_pre(root, ector, NULL);
 
-   //3. draw into the buffer
+   //initialize buffer
    context = evas_common_draw_context_new();
    evas_common_draw_context_set_render_op(context, _EVAS_RENDER_COPY);
    evas_common_draw_context_set_color(context, 255, 255, 255, 255);
@@ -541,6 +550,7 @@ _render_to_buffer(Evas_Object_Protected_Data *obj, Efl_Canvas_Vg_Data *pd,
                                               ector,
                                               0, 0,
                                               do_async);
+   //draw on buffer
    _evas_vg_render(obj, pd,
                    engine, buffer,
                    context, surface,
@@ -554,10 +564,12 @@ _render_to_buffer(Evas_Object_Protected_Data *obj, Efl_Canvas_Vg_Data *pd,
 
    evas_common_draw_context_free(context);
 
+   //caching buffer only for first and last frames.
    if (buffer_created)
      {
-        pd->cache_key = key;
-        obj->layer->evas->engine.func->ector_surface_cache_set(engine, key, buffer);
+        if (pd->frame_index == 0 ||
+            (pd->frame_index == (int) evas_cache_vg_anim_frame_count_get(pd->vg_entry) - 1))
+          obj->layer->evas->engine.func->ector_surface_cache_set(engine, key, buffer);
      }
 
    return buffer;
@@ -593,35 +605,26 @@ _cache_vg_entry_render(Evas_Object_Protected_Data *obj,
 {
    Vg_Cache_Entry *vg_entry = pd->vg_entry;
    Efl_VG *root;
-   void *buffer;
 
    // if the size changed in between path set and the draw call;
+
    if ((vg_entry->w != w) ||
        (vg_entry->h != h))
      {
          vg_entry = evas_cache_vg_entry_resize(vg_entry, w, h);
          evas_cache_vg_entry_del(pd->vg_entry);
          pd->vg_entry = vg_entry;
-         pd->cache_key = NULL;
      }
    root = evas_cache_vg_tree_get(vg_entry, pd->frame_index);
    if (!root) return;
-   buffer = obj->layer->evas->engine.func->ector_surface_cache_get(engine, pd->cache_key);
 
-   // if the buffer is not created yet
+   void *buffer = obj->layer->evas->engine.func->ector_surface_cache_get(engine, root);
+
    if (!buffer)
-     {
-        // render to the buffer
-        buffer = _render_to_buffer(obj, pd,
-                                   engine, surface,
-                                   root,
-                                   w, h,
-                                   root,
-                                   buffer,
-                                   do_async);
-     }
+     buffer = _render_to_buffer(obj, pd, engine, surface, root, w, h, root, NULL, do_async);
    else
-     obj->layer->evas->engine.func->ector_surface_cache_drop(engine, pd->cache_key);
+     //cache reference was increased when we get the cache.
+     obj->layer->evas->engine.func->ector_surface_cache_drop(engine, root);
 
    _render_buffer_to_screen(obj,
                             engine, output, context, surface,
@@ -637,20 +640,19 @@ _user_vg_entry_render(Evas_Object_Protected_Data *obj,
                       int x, int y, int w, int h, Eina_Bool do_async)
 {
    Vg_User_Entry *user_entry = pd->user_entry;
-   void *buffer;
 
-   //if the size dosen't match
+   //if the size doesn't match, drop previous cache surface.
    if ((user_entry->w != w ) ||
        (user_entry->h != h))
      {
-         obj->layer->evas->engine.func->ector_surface_cache_drop(engine, user_entry);
+         obj->layer->evas->engine.func->ector_surface_cache_drop(engine, user_entry->root);
          user_entry->w = w;
          user_entry->h = h;
-         pd->user_entry = user_entry;
      }
 
    //if the buffer is not created yet
-   buffer = obj->layer->evas->engine.func->ector_surface_cache_get(engine, pd->cache_key);
+   void *buffer = obj->layer->evas->engine.func->ector_surface_cache_get(engine, user_entry->root);
+
    if (!buffer)
      {
         // render to the buffer
@@ -673,7 +675,8 @@ _user_vg_entry_render(Evas_Object_Protected_Data *obj,
                                      user_entry,
                                      buffer,
                                      do_async);
-        obj->layer->evas->engine.func->ector_surface_cache_drop(engine, pd->cache_key);
+        //cache reference was increased when we get the cache.
+        obj->layer->evas->engine.func->ector_surface_cache_drop(engine, user_entry->root);
      }
 
    _render_buffer_to_screen(obj,
@@ -932,7 +935,6 @@ _efl_canvas_vg_efl_gfx_image_animation_controller_animated_frame_set(Eo *eo_obj,
    if (pd->frame_index == frame_index) return EINA_TRUE;
 
    //Image is changed, drop previous cached image.
-   pd->cache_key = NULL;
    pd->frame_index = frame_index;
    pd->changed = EINA_TRUE;
    evas_object_change(eo_obj, efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS));
index 2de2d74..8e60fc0 100644 (file)
@@ -53,7 +53,6 @@ struct _Efl_Canvas_Vg_Data
    int                        frame_index;
    Eina_File                 *file;
    Eina_Stringshare          *key;
-   void                      *cache_key; // store ector surface cache key
 
    Eina_Bool                  changed : 1;
 };
index 807ed9b..936e620 100644 (file)
@@ -48,6 +48,7 @@ generic_cache_data_set(Generic_Cache *cache, void *key, void *surface)
    eina_hash_add(cache->hash, &key, entry);
    cache->lru_list = eina_list_prepend(cache->lru_list, entry);
    count = eina_list_count(cache->lru_list);
+
    if (count > LRU_CACHE_LIMIT)
    {
       entry = eina_list_data_get(eina_list_last(cache->lru_list));
index 9c71b16..23f6cd4 100644 (file)
@@ -216,28 +216,26 @@ _cached_root_get(Vg_Cache_Entry *vg_entry, unsigned int frame_num)
 {
    Vg_File_Data *vfd = vg_entry->vfd;
 
-   if (vfd->static_viewbox || ((vfd->view_box.w == vg_entry->w) && (vfd->view_box.h == vg_entry->h)))
+   //Case 1: Animatable
+   if (vfd->anim_data)
      {
-        //Case 1: Animatable
-        if (vfd->anim_data)
+        //Start frame
+        if (vg_entry->root[1] && frame_num == 0)
           {
-             //Start frame
-             if (vg_entry->root[1] && frame_num == 0)
-               {
-                  return vg_entry->root[1];
-               }
-             else if (vg_entry->root[2] && (frame_num == (vfd->anim_data->frame_cnt - 1)))
-               {
-                  return vg_entry->root[2];
-               }
+             return vg_entry->root[1];
           }
-        //Case 2: Static
-        else
+        else if (vg_entry->root[2] && (frame_num == (vfd->anim_data->frame_cnt - 1)))
           {
-             if (vg_entry->root[0])
-               return vg_entry->root[0];
+             return vg_entry->root[2];
           }
      }
+   //Case 2: Static
+   else
+     {
+        if (vg_entry->root[0])
+          return vg_entry->root[0];
+     }
+
    return NULL;
 }
 
@@ -246,8 +244,22 @@ _caching_root_update(Vg_Cache_Entry *vg_entry)
 {
    Vg_File_Data *vfd = vg_entry->vfd;
 
-   if (vg_entry->root[0]) efl_unref(vg_entry->root[0]);
-   vg_entry->root[0] = efl_ref(vfd->root);
+   /* Optimization: static viewbox may have same root data regardless of size.
+      So we can't use the root data directly, but copy it for each vg_entries.
+      In the meantime, non-static viewbox root data may have difference instance for each
+      size. So it's affordable to share the root data for each vg_entries. */
+   if (vfd->static_viewbox)
+     {
+        /* TODO: Yet trivial but still we may have a better solution to
+           avoid this unnecessary copy. If the ector surface key is not
+           to this root pointer. */
+        vg_entry->root[0] = efl_duplicate(vfd->root);
+     }
+   else if (vg_entry->root[0] != vfd->root)
+     {
+        if (vg_entry->root[0]) efl_unref(vg_entry->root[0]);
+        vg_entry->root[0] = efl_ref(vfd->root);
+     }
 
    //Animatable?
    if (!vfd->anim_data) return;
@@ -255,13 +267,20 @@ _caching_root_update(Vg_Cache_Entry *vg_entry)
    //Start frame
    if (vfd->anim_data->frame_num == 0)
      {
-        if (vg_entry->root[1]) efl_unref(vg_entry->root[1]);
-        vg_entry->root[1] = efl_ref(vfd->root);
+        if (vg_entry->root[1] != vfd->root)
+          {
+             if (vg_entry->root[1]) efl_unref(vg_entry->root[1]);
+             vg_entry->root[1] = efl_ref(vfd->root);
+          }
      }
+   //End frame
    else if (vfd->anim_data->frame_num == (vfd->anim_data->frame_cnt - 1))
      {
-        if (vg_entry->root[2]) efl_unref(vg_entry->root[2]);
-        vg_entry->root[2] = efl_ref(vfd->root);
+        if (vg_entry->root[2] != vfd->root)
+          {
+             if (vg_entry->root[2]) efl_unref(vg_entry->root[2]);
+             vg_entry->root[2] = efl_ref(vfd->root);
+          }
      }
 }
 
@@ -410,6 +429,7 @@ evas_cache_vg_anim_duration_get(const Vg_Cache_Entry* vg_entry)
 unsigned int
 evas_cache_vg_anim_frame_count_get(const Vg_Cache_Entry* vg_entry)
 {
+   if (!vg_entry) return 0;
    Vg_File_Data *vfd = vg_entry->vfd;
    if (!vfd || !vfd->anim_data) return 0;
    return vfd->anim_data->frame_cnt;