evas vg: revise buffer caching method. 23/220123/1
authorHermet Park <hermetpark@gmail.com>
Fri, 13 Dec 2019 08:22:54 +0000 (17:22 +0900)
committerHermet Park <hermetpark@gmail.com>
Fri, 13 Dec 2019 08:28:03 +0000 (17:28 +0900)
for better precise buffer cache key,
We make a unique key name combining root node + size + frame index.

Now, we can reuse the root node for animation and caching buffers.

Change-Id: I4370b8b234dd47279370edbb557194b8f6d925c3

src/lib/evas/canvas/efl_canvas_vg_object.c
src/lib/evas/canvas/evas_vg_private.h
src/lib/evas/vg/evas_vg_cache.c

index 740cfac..83a59bd 100644 (file)
@@ -144,6 +144,8 @@ _efl_canvas_vg_object_root_node_set(Eo *eo_obj, Efl_Canvas_Vg_Object_Data *pd, E
    // detach/free the old root_node
    if (pd->user_entry && pd->user_entry->root)
      {
+        // drop any surface cache attached to it.
+        ENFN->ector_surface_cache_drop(_evas_engine_context(obj->layer->evas), pd->user_entry->root);
         efl_canvas_vg_node_vg_obj_set(pd->user_entry->root, NULL, NULL);
         efl_replace(&pd->user_entry->root, NULL);
      }
@@ -166,8 +168,6 @@ _efl_canvas_vg_object_root_node_set(Eo *eo_obj, Efl_Canvas_Vg_Object_Data *pd, E
      }
    else if (pd->user_entry)
      {
-        // drop any surface cache attached to it.
-        ENFN->ector_surface_cache_drop(_evas_engine_context(obj->layer->evas), pd->user_entry->root);
         free(pd->user_entry);
         pd->user_entry = NULL;
      }
@@ -345,7 +345,6 @@ _efl_canvas_vg_object_efl_object_invalidate(Eo *eo_obj, Efl_Canvas_Vg_Object_Dat
    if (pd->user_entry)
      {
         Vg_User_Entry *user_entry = pd->user_entry;
-
         ENFN->ector_surface_cache_drop(ENC, user_entry->root);
         free(pd->user_entry);
      }
@@ -492,8 +491,8 @@ _evas_vg_render(Evas_Object_Protected_Data *obj, Efl_Canvas_Vg_Object_Data *pd,
 //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_Object_Data *pd,
-                  void *engine, Efl_VG *root, int w, int h, void *buffer,
-                  Eina_Bool do_async, Eina_Bool cacheable)
+                  void *engine, Efl_VG *root, int w, int h, void *buffer, void *ckey,
+                  Eina_Bool do_async)
 {
    Ector_Surface *ector;
    RGBA_Draw_Context *context;
@@ -534,11 +533,8 @@ _render_to_buffer(Evas_Object_Protected_Data *obj, Efl_Canvas_Vg_Object_Data *pd
    ENFN->ector_end(engine, buffer, context, ector, do_async);
    evas_common_draw_context_free(context);
 
-   if (buffer_created && cacheable)
-     {
-        //Use root as a cache key.
-        ENFN->ector_surface_cache_set(engine, root, buffer);
-     }
+   if (buffer_created && ckey)
+     ENFN->ector_surface_cache_set(engine, ckey, buffer);
 
    return buffer;
 }
@@ -579,8 +575,8 @@ _cache_vg_entry_render(Evas_Object_Protected_Data *obj,
    Vg_Cache_Entry *vg_entry = pd->vg_entry;
    Efl_VG *root;
    Eina_Position2D offset = {0, 0};  //Offset after keeping aspect ratio.
-   Eina_Bool drop_cache = EINA_FALSE;
    void *buffer = NULL;
+   void *key = NULL;
 
    evas_cache_vg_entry_value_provider_update(pd->vg_entry, efl_key_data_get(obj->object, "_vg_value_providers"));
 
@@ -616,7 +612,23 @@ _cache_vg_entry_render(Evas_Object_Protected_Data *obj,
         //Size is changed, cached data is invalid.
         if ((size.w != vg_entry->w) || (size.h != vg_entry->h))
           {
-             drop_cache = EINA_TRUE;
+//Not necessary, but this might be helpful for precise caching.
+#if 0
+             if (cacheable)
+               {
+                  //if the size doesn't match, drop previous cache surface.
+                  key = evas_cache_vg_surface_key_get(pd->vg_entry->root, vg_entry->w, vg_entry->h, 0);
+                  if (key) ENFN->ector_surface_cache_drop(engine, key);
+
+                  //Animatable... Try to drop the last frame image.
+                  int last_frame = (int) (evas_cache_vg_anim_frame_count_get(pd->vg_entry) - 1);
+                  if (last_frame > 0)
+                    {
+                       key = evas_cache_vg_surface_key_get(pd->vg_entry->root, vg_entry->w, vg_entry->h, last_frame);
+                       if (key) ENFN->ector_surface_cache_drop(engine, key);
+                    }
+               }
+#endif
              vg_entry = evas_cache_vg_entry_resize(vg_entry, size.w, size.h);
              evas_cache_vg_entry_del(pd->vg_entry);
              pd->vg_entry = vg_entry;
@@ -629,27 +641,25 @@ _cache_vg_entry_render(Evas_Object_Protected_Data *obj,
         if (offset.y > 0) offset.y /= 2;
         w = size.w;
         h = size.h;
-
      }
    root = evas_cache_vg_tree_get(vg_entry, pd->frame_idx);
    if (!root) return;
 
    if (cacheable)
      {
-        //if the size doesn't match, drop previous cache surface.
-        if (drop_cache)
-          ENFN->ector_surface_cache_drop(engine, (void *) root);
-        //Cache Hit!
-        else
-          buffer = ENFN->ector_surface_cache_get(engine, (void *) root);
+        key = evas_cache_vg_surface_key_get(root, w, h, pd->frame_idx);
+        if (key) buffer = ENFN->ector_surface_cache_get(engine, key);
      }
 
    if (!buffer)
-     buffer = _render_to_buffer(obj, pd, engine, root, w, h, NULL,
-                                do_async, cacheable);
+     {
+        buffer = _render_to_buffer(obj, pd, engine, root, w, h, NULL, key, do_async);
+     }
    else
-     //cache reference was increased when we get the cache.
-     ENFN->ector_surface_cache_drop(engine, (void *) root);
+     {
+        //cache reference was increased when we get the cache.
+        if (key) ENFN->ector_surface_cache_drop(engine, key);
+     }
 
    _render_buffer_to_screen(obj,
                             engine, output, context, surface,
@@ -662,8 +672,7 @@ static void
 _user_vg_entry_render(Evas_Object_Protected_Data *obj,
                       Efl_Canvas_Vg_Object_Data *pd,
                       void *engine, void *output, void *context, void *surface,
-                      int x, int y, int w, int h, Eina_Bool do_async,
-                      Eina_Bool cacheable)
+                      int x, int y, int w, int h, Eina_Bool do_async)
 {
    Vg_User_Entry *user_entry = pd->user_entry;
 
@@ -679,14 +688,13 @@ _user_vg_entry_render(Evas_Object_Protected_Data *obj,
    //if the buffer is not created yet
    void *buffer = NULL;
 
-   if (cacheable)
      buffer = ENFN->ector_surface_cache_get(engine, user_entry->root);
 
    if (!buffer)
      {
         // render to the buffer
         buffer = _render_to_buffer(obj, pd, engine, user_entry->root,
-                                   w, h, buffer, do_async, cacheable);
+                                   w, h, buffer, user_entry->root, do_async);
      }
    else
      {
@@ -694,8 +702,8 @@ _user_vg_entry_render(Evas_Object_Protected_Data *obj,
         if (pd->changed)
           buffer = _render_to_buffer(obj, pd, engine,
                                      user_entry->root,
-                                     w, h, buffer,
-                                     do_async, EINA_FALSE);
+                                     w, h, buffer, NULL,
+                                     do_async);
         //cache reference was increased when we get the cache.
         ENFN->ector_surface_cache_drop(engine, user_entry->root);
      }
@@ -704,7 +712,7 @@ _user_vg_entry_render(Evas_Object_Protected_Data *obj,
                             engine, output, context, surface,
                             buffer,
                             x, y, w, h,
-                            do_async, cacheable);
+                            do_async, EINA_TRUE);
 }
 
 static void
@@ -729,8 +737,12 @@ _efl_canvas_vg_object_render(Evas_Object *eo_obj EINA_UNUSED,
    //Cache surface?
    Eina_Bool cacheable = EINA_FALSE;
 
-   /* Try caching buffer only for static images. */
-   if (evas_cache_vg_anim_frame_count_get(pd->vg_entry) == 0)
+   /* Try caching buffer only for first and last frames
+      because it's an overhead task if it caches all frame images.
+      We assume the first and last frame images are the most resusable
+      in generic scenarios. */
+   if (pd->frame_idx == 0 ||
+       (pd->frame_idx == (int) (evas_cache_vg_anim_frame_count_get(pd->vg_entry) - 1)))
      cacheable = EINA_TRUE;
 
    if (pd->vg_entry)
@@ -745,7 +757,7 @@ _efl_canvas_vg_object_render(Evas_Object *eo_obj EINA_UNUSED,
         _user_vg_entry_render(obj, pd,
                               engine, output, context, surface,
                               obj->cur->geometry.x + x, obj->cur->geometry.y + y,
-                              obj->cur->geometry.w, obj->cur->geometry.h, do_async, cacheable);
+                              obj->cur->geometry.w, obj->cur->geometry.h, do_async);
      }
    pd->changed = EINA_FALSE;
 }
index 2a1dbfa..a6a2714 100644 (file)
@@ -15,6 +15,7 @@ typedef struct _Vg_Cache
 {
    Eina_Hash             *vfd_hash;
    Eina_Hash             *vg_entry_hash;
+   Eina_List             *vg_surface_keys;
    int                    ref;
 } Vg_Cache;
 
@@ -154,6 +155,7 @@ Eina_Bool                   evas_cache_vg_anim_sector_set(const Vg_Cache_Entry*
 Eina_Bool                   evas_cache_vg_anim_sector_get(const Vg_Cache_Entry* vg_entry, const char *name, int* startframe, int* endframe);
 unsigned int                evas_cache_vg_anim_frame_count_get(const Vg_Cache_Entry *vg_entry);
 Eina_Size2D                 evas_cache_vg_entry_default_size_get(const Vg_Cache_Entry *vg_entry);
+void *                      evas_cache_vg_surface_key_get(Efl_Canvas_Vg_Node *root, int w, int h, int frame_idx);
 void                        efl_canvas_vg_node_vg_obj_set(Efl_VG *node, Efl_VG *vg_obj, Efl_Canvas_Vg_Object_Data *vd);
 void                        efl_canvas_vg_node_change(Efl_VG *node);
 void                        efl_canvas_vg_container_vg_obj_update(Efl_VG *obj, Efl_Canvas_Vg_Node_Data *nd);
index 5c0242d..2ef31bd 100644 (file)
@@ -269,12 +269,45 @@ evas_cache_vg_init(void)
    vg_cache->ref++;
 }
 
+void *
+evas_cache_vg_surface_key_get(Efl_Canvas_Vg_Node *root, int w, int h, int frame_idx)
+{
+   //This make a unique key pointer by arguments.
+   Eina_Strbuf *hash_key = eina_strbuf_new();
+   eina_strbuf_append_printf(hash_key, "%p/%d/%d/%d", root, w, h, frame_idx);
+   const char *new_key = eina_strbuf_string_get(hash_key);
+   if (!new_key) return NULL;
+
+   Eina_List *l;
+   char *key;
+   EINA_LIST_FOREACH(vg_cache->vg_surface_keys, l, key)
+     {
+        //Exisiting key!
+        if (!strcmp(key, new_key))
+          {
+             eina_strbuf_free(hash_key);
+             return key;
+          }
+     }
+
+   //New key comes.
+   key = eina_strbuf_string_steal(hash_key);
+   vg_cache->vg_surface_keys = eina_list_append(vg_cache->vg_surface_keys, key);
+   return (void *) key;
+}
+
 void
 evas_cache_vg_shutdown(void)
 {
    if (!vg_cache) return;
    vg_cache->ref--;
    if (vg_cache->ref > 0) return;
+
+   char *key;
+   EINA_LIST_FREE(vg_cache->vg_surface_keys, key)
+     free(key);
+   eina_list_free(vg_cache->vg_surface_keys);
+
    eina_hash_free(vg_cache->vfd_hash);
    eina_hash_free(vg_cache->vg_entry_hash);
    free(vg_cache);