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 // detach/free the old root_node
if (pd->user_entry && pd->user_entry->root) 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_canvas_vg_node_vg_obj_set(pd->user_entry->root, NULL, NULL);
efl_replace(&pd->user_entry->root, 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) 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); free(pd->user_entry);
pd->user_entry = NULL; 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) if (pd->user_entry)
{ {
Vg_User_Entry *user_entry = pd->user_entry; Vg_User_Entry *user_entry = pd->user_entry;
ENFN->ector_surface_cache_drop(ENC, user_entry->root); ENFN->ector_surface_cache_drop(ENC, user_entry->root);
free(pd->user_entry); 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. //renders a vg_tree to an offscreen buffer and push it to the cache.
static void * static void *
_render_to_buffer(Evas_Object_Protected_Data *obj, Efl_Canvas_Vg_Object_Data *pd, _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, void *engine, Efl_VG *root, int w, int h, void *buffer, void *ckey,
Eina_Bool do_async, Eina_Bool cacheable) Eina_Bool do_async)
{ {
Ector_Surface *ector; Ector_Surface *ector;
RGBA_Draw_Context *context; 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); ENFN->ector_end(engine, buffer, context, ector, do_async);
evas_common_draw_context_free(context); evas_common_draw_context_free(context);
if (buffer_created && cacheable) if (buffer_created && ckey)
{ ENFN->ector_surface_cache_set(engine, ckey, buffer);
//Use root as a cache key.
ENFN->ector_surface_cache_set(engine, root, buffer);
}
return buffer; return buffer;
} }
@ -579,8 +575,8 @@ _cache_vg_entry_render(Evas_Object_Protected_Data *obj,
Vg_Cache_Entry *vg_entry = pd->vg_entry; Vg_Cache_Entry *vg_entry = pd->vg_entry;
Efl_VG *root; Efl_VG *root;
Eina_Position2D offset = {0, 0}; //Offset after keeping aspect ratio. Eina_Position2D offset = {0, 0}; //Offset after keeping aspect ratio.
Eina_Bool drop_cache = EINA_FALSE;
void *buffer = NULL; 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")); 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. //Size is changed, cached data is invalid.
if ((size.w != vg_entry->w) || (size.h != vg_entry->h)) 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); vg_entry = evas_cache_vg_entry_resize(vg_entry, size.w, size.h);
evas_cache_vg_entry_del(pd->vg_entry); evas_cache_vg_entry_del(pd->vg_entry);
pd->vg_entry = 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; if (offset.y > 0) offset.y /= 2;
w = size.w; w = size.w;
h = size.h; h = size.h;
} }
root = evas_cache_vg_tree_get(vg_entry, pd->frame_idx); root = evas_cache_vg_tree_get(vg_entry, pd->frame_idx);
if (!root) return; if (!root) return;
if (cacheable) if (cacheable)
{ {
//if the size doesn't match, drop previous cache surface. key = evas_cache_vg_surface_key_get(root, w, h, pd->frame_idx);
if (drop_cache) if (key) buffer = ENFN->ector_surface_cache_get(engine, key);
ENFN->ector_surface_cache_drop(engine, (void *) root);
//Cache Hit!
else
buffer = ENFN->ector_surface_cache_get(engine, (void *) root);
} }
if (!buffer) 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 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, _render_buffer_to_screen(obj,
engine, output, context, surface, engine, output, context, surface,
@ -662,8 +672,7 @@ static void
_user_vg_entry_render(Evas_Object_Protected_Data *obj, _user_vg_entry_render(Evas_Object_Protected_Data *obj,
Efl_Canvas_Vg_Object_Data *pd, Efl_Canvas_Vg_Object_Data *pd,
void *engine, void *output, void *context, void *surface, void *engine, void *output, void *context, void *surface,
int x, int y, int w, int h, Eina_Bool do_async, int x, int y, int w, int h, Eina_Bool do_async)
Eina_Bool cacheable)
{ {
Vg_User_Entry *user_entry = pd->user_entry; 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 //if the buffer is not created yet
void *buffer = NULL; void *buffer = NULL;
if (cacheable)
buffer = ENFN->ector_surface_cache_get(engine, user_entry->root); buffer = ENFN->ector_surface_cache_get(engine, user_entry->root);
if (!buffer) if (!buffer)
{ {
// render to the buffer // render to the buffer
buffer = _render_to_buffer(obj, pd, engine, user_entry->root, 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 else
{ {
@ -694,8 +702,8 @@ _user_vg_entry_render(Evas_Object_Protected_Data *obj,
if (pd->changed) if (pd->changed)
buffer = _render_to_buffer(obj, pd, engine, buffer = _render_to_buffer(obj, pd, engine,
user_entry->root, user_entry->root,
w, h, buffer, w, h, buffer, NULL,
do_async, EINA_FALSE); do_async);
//cache reference was increased when we get the cache. //cache reference was increased when we get the cache.
ENFN->ector_surface_cache_drop(engine, user_entry->root); 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, engine, output, context, surface,
buffer, buffer,
x, y, w, h, x, y, w, h,
do_async, cacheable); do_async, EINA_TRUE);
} }
static void static void
@ -729,8 +737,12 @@ _efl_canvas_vg_object_render(Evas_Object *eo_obj EINA_UNUSED,
//Cache surface? //Cache surface?
Eina_Bool cacheable = EINA_FALSE; Eina_Bool cacheable = EINA_FALSE;
/* Try caching buffer only for static images. */ /* Try caching buffer only for first and last frames
if (evas_cache_vg_anim_frame_count_get(pd->vg_entry) == 0) 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; cacheable = EINA_TRUE;
if (pd->vg_entry) 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, _user_vg_entry_render(obj, pd,
engine, output, context, surface, engine, output, context, surface,
obj->cur->geometry.x + x, obj->cur->geometry.y + y, 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; pd->changed = EINA_FALSE;
} }

View File

@ -15,6 +15,7 @@ typedef struct _Vg_Cache
{ {
Eina_Hash *vfd_hash; Eina_Hash *vfd_hash;
Eina_Hash *vg_entry_hash; Eina_Hash *vg_entry_hash;
Eina_List *vg_surface_keys;
int ref; int ref;
} Vg_Cache; } 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); 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); 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); 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_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_node_change(Efl_VG *node);
void efl_canvas_vg_container_vg_obj_update(Efl_VG *obj, Efl_Canvas_Vg_Node_Data *nd); 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++; 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 void
evas_cache_vg_shutdown(void) evas_cache_vg_shutdown(void)
{ {
if (!vg_cache) return; if (!vg_cache) return;
vg_cache->ref--; vg_cache->ref--;
if (vg_cache->ref > 0) return; 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->vfd_hash);
eina_hash_free(vg_cache->vg_entry_hash); eina_hash_free(vg_cache->vg_entry_hash);
free(vg_cache); free(vg_cache);