evas vg: revise buffer caching method.

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.
This commit is contained in:
Hermet Park 2019-12-13 17:22:54 +09:00
parent 3372a701d3
commit 4f99f9f2bf
3 changed files with 80 additions and 33 deletions

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

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

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