evas vg: revise basic vg cache logic.

There was a big trouble that vg cache didn't free cached data properly.
Plus, there was a unnecessary copy of vg tree data.

This revised version is a improvement of our evas vg cache
in stable and optmization.
This commit is contained in:
Hermet Park 2018-12-07 18:42:45 +09:00
parent 072f5a2fa0
commit 950059ce9a
7 changed files with 210 additions and 123 deletions

View File

@ -113,27 +113,27 @@ _evas_vg_resize(void *data, const Efl_Event *ev)
_update_vgtree_viewport(ev->object, pd);
}
/* the actual api call to add a vector graphic object */
EAPI Evas_Object *
evas_object_vg_add(Evas *e)
{
e = evas_find(e);
EINA_SAFETY_ON_FALSE_RETURN_VAL(efl_isa(e, EVAS_CANVAS_CLASS), NULL);
// TODO: Ask backend to return the main Ector_Surface
return efl_add(MY_CLASS, e, efl_canvas_object_legacy_ctor(efl_added));
}
EOLIAN static Efl_VG *
_efl_canvas_vg_object_root_node_get(const Eo *obj EINA_UNUSED, Efl_Canvas_Vg_Object_Data *pd)
_efl_canvas_vg_object_root_node_get(const Eo *obj, Efl_Canvas_Vg_Object_Data *pd)
{
Efl_VG *root = NULL;
if (pd->vg_entry)
root = evas_cache_vg_tree_get(pd->vg_entry);
else if (pd->user_entry)
root = pd->user_entry->root;
else
root = pd->root;
{
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);
}
else if (pd->user_entry) root = pd->user_entry->root;
else root = pd->root;
return root;
}
@ -256,7 +256,7 @@ _efl_canvas_vg_object_viewbox_align_get(const Eo *obj EINA_UNUSED, Efl_Canvas_Vg
}
EOLIAN static Eina_Bool
_efl_canvas_vg_object_efl_file_file_set(Eo *obj, Efl_Canvas_Vg_Object_Data *pd, const char *file, const char *key)
_efl_canvas_vg_object_efl_file_file_set(Eo *eo_obj, Efl_Canvas_Vg_Object_Data *pd, const char *file, const char *key)
{
Vg_Cache_Entry *old_entry;
int w, h;
@ -264,10 +264,18 @@ _efl_canvas_vg_object_efl_file_file_set(Eo *obj, Efl_Canvas_Vg_Object_Data *pd,
if (!file) return EINA_FALSE;
old_entry = pd->vg_entry;
evas_object_geometry_get(obj, NULL, NULL, &w, &h);
pd->vg_entry = evas_cache_vg_entry_find(file, key, w, h);
if (pd->vg_entry != old_entry)
evas_object_change(obj, efl_data_scope_get(obj, EFL_CANVAS_OBJECT_CLASS));
Evas_Object_Protected_Data *obj;
obj = efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS);
if (file)
pd->vg_entry = evas_cache_vg_entry_create(file, key,
obj->cur->geometry.w,
obj->cur->geometry.h);
else
pd->vg_entry = NULL;
evas_object_change(eo_obj, obj);
evas_cache_vg_entry_del(old_entry);
return EINA_TRUE;
@ -321,6 +329,7 @@ _efl_canvas_vg_object_efl_object_destructor(Eo *eo_obj, Efl_Canvas_Vg_Object_Dat
if (pd->user_entry) free(pd->user_entry);
pd->user_entry = NULL;
evas_cache_vg_entry_del(pd->vg_entry);
efl_destructor(efl_super(eo_obj, MY_CLASS));
}
@ -472,16 +481,15 @@ _cache_vg_entry_render(Evas_Object_Protected_Data *obj,
int x, int y, int w, int h, Eina_Bool do_async)
{
Vg_Cache_Entry *vg_entry = pd->vg_entry;
Efl_VG *root, *dupe_root;
Efl_VG *root;
// if the size changed in between path set and the draw call;
if ((vg_entry->w != w) ||
(vg_entry->h != h))
{
evas_cache_vg_entry_del(vg_entry);
vg_entry = evas_cache_vg_entry_find(vg_entry->file, vg_entry->key,
w, 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;
}
root = evas_cache_vg_tree_get(vg_entry);
@ -490,18 +498,8 @@ _cache_vg_entry_render(Evas_Object_Protected_Data *obj,
void *buffer = ENFN->ector_surface_cache_get(engine, root);
if (!buffer)
{
dupe_root = efl_duplicate(root);
// render to the buffer
buffer = _render_to_buffer(obj, pd,
engine, surface,
dupe_root,
w, h,
root,
buffer,
do_async);
efl_unref(dupe_root);
}
buffer = _render_to_buffer(obj, pd, engine, surface, root, w, h, root, NULL,
do_async);
else
//cache reference was increased when we get the cache.
ENFN->ector_surface_cache_drop(engine, root);
@ -525,13 +523,14 @@ _user_vg_entry_render(Evas_Object_Protected_Data *obj,
if ((user_entry->w != w ) ||
(user_entry->h != h))
{
ENFN->ector_surface_cache_drop(engine, user_entry);
ENFN->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
void *buffer = ENFN->ector_surface_cache_get(engine, user_entry);
void *buffer = ENFN->ector_surface_cache_get(engine, user_entry->root);
if (!buffer)
{
// render to the buffer
@ -554,7 +553,8 @@ _user_vg_entry_render(Evas_Object_Protected_Data *obj,
user_entry,
buffer,
do_async);
ENFN->ector_surface_cache_drop(engine, user_entry);
//cache reference was increased when we get the cache.
ENFN->ector_surface_cache_drop(engine, user_entry->root);
}
_render_buffer_to_screen(obj,
@ -751,4 +751,14 @@ _efl_canvas_vg_object_was_opaque(Evas_Object *eo_obj EINA_UNUSED,
return 0;
}
/* the actual api call to add a vector graphic object */
EAPI Evas_Object *
evas_object_vg_add(Evas *e)
{
e = evas_find(e);
EINA_SAFETY_ON_FALSE_RETURN_VAL(efl_isa(e, EVAS_CANVAS_CLASS), NULL);
// TODO: Ask backend to return the main Ector_Surface
return efl_add(MY_CLASS, e, efl_canvas_object_legacy_ctor(efl_added));
}
#include "efl_canvas_vg_object.eo.c"

View File

@ -25,6 +25,8 @@ typedef struct _Vg_Cache_Entry
int h;
Efl_VG *root;
int ref;
Vg_File_Data *vfd;
} Vg_Cache_Entry;
// holds the vg tree info set by the user
@ -100,9 +102,10 @@ struct _Efl_Canvas_Vg_Interpolation
void evas_cache_vg_init(void);
void evas_cache_vg_shutdown(void);
Vg_Cache_Entry* evas_cache_vg_entry_find(const char *file, const char *key, int w, int h);
Efl_VG* evas_cache_vg_tree_get(Vg_Cache_Entry *svg_entry);
void evas_cache_vg_entry_del(Vg_Cache_Entry *svg_entry);
Vg_Cache_Entry* evas_cache_vg_entry_resize(Vg_Cache_Entry *entry, int w, int h);
Vg_Cache_Entry* evas_cache_vg_entry_create(const char *file, const char *key, int w, int h);
Efl_VG* evas_cache_vg_tree_get(Vg_Cache_Entry *vg_entry);
void evas_cache_vg_entry_del(Vg_Cache_Entry *vg_entry);
Vg_File_Data * evas_cache_vg_file_open(const char *file, const char *key);
Eina_Bool evas_cache_vg_file_save(Efl_VG *root, int w, int h, const char *file, const char *key, const char *flags);
Eina_Bool evas_cache_vg_entry_file_save(Vg_Cache_Entry *vg_entry, const char *file, const char *key, const char *flags);

View File

@ -1508,6 +1508,9 @@ struct _Vg_File_Data
Efl_VG *root;
Evas_Vg_Load_Func *loader;
Eina_Rectangle view_box;
int ref;
Eina_Bool static_viewbox: 1;
Eina_Bool preserve_aspect : 1; //Used in SVG
};

View File

@ -69,7 +69,7 @@ _find_loader_module(const char *file)
return em;
}
Vg_File_Data *
static Vg_File_Data *
_vg_load_from_file(const char *file, const char *key)
{
Evas_Module *em;
@ -136,9 +136,8 @@ _find_saver_module(const char *file)
static void
_evas_cache_vg_data_free_cb(void *data)
{
Vg_File_Data *val = data;
efl_unref(val->root);
free(val);
Vg_File_Data *vfd = data;
vfd->loader->file_close(vfd);
}
static void
@ -146,7 +145,21 @@ _evas_cache_vg_entry_free_cb(void *data)
{
Vg_Cache_Entry *vg_entry = data;
eina_stringshare_del(vg_entry->file);
if (vg_entry->vfd)
{
vg_entry->vfd->ref--;
if (vg_entry->vfd->ref <= 0)
{
Eina_Strbuf *hash_key = eina_strbuf_new();
eina_strbuf_append_printf(hash_key, "%s/%s",
vg_entry->file,
vg_entry->key);
if (!eina_hash_del(vg_cache->vfd_hash, eina_strbuf_string_get(hash_key), vg_entry->vfd))
ERR("Failed to delete vfd = (%p) from hash", vg_entry->vfd);
eina_strbuf_free(hash_key);
}
}
eina_stringshare_del(vg_entry->key);
free(vg_entry->hash_key);
efl_unref(vg_entry->root);
@ -194,6 +207,65 @@ _vg_file_save(Vg_File_Data *vfd, const char *file, const char *key, const char *
return EINA_TRUE;
}
static Efl_VG*
_cached_root_get(Vg_Cache_Entry *vg_entry)
{
return vg_entry->root;
}
static void
_caching_root_update(Vg_Cache_Entry *vg_entry)
{
Vg_File_Data *vfd = vg_entry->vfd;
/* 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 = efl_duplicate(vfd->root);
}
else if (vg_entry->root != vfd->root)
{
if (vg_entry->root) efl_unref(vg_entry->root);
vg_entry->root = efl_ref(vfd->root);
}
}
static void
_local_transform(Efl_VG *root, double w, double h, Vg_File_Data *vfd)
{
double sx = 0, sy= 0, scale;
Eina_Matrix3 m;
if (!vfd->static_viewbox) return;
if (vfd->view_box.w == w && vfd->view_box.h == h) return;
sx = w / vfd->view_box.w;
sy = h / vfd->view_box.h;
scale = sx < sy ? sx : sy;
eina_matrix3_identity(&m);
// align hcenter and vcenter
if (vfd->preserve_aspect)
{
eina_matrix3_translate(&m, (w - vfd->view_box.w * scale)/2.0, (h - vfd->view_box.h * scale)/2.0);
eina_matrix3_scale(&m, scale, scale);
eina_matrix3_translate(&m, -vfd->view_box.x, -vfd->view_box.y);
}
else
{
eina_matrix3_scale(&m, sx, sy);
eina_matrix3_translate(&m, -vfd->view_box.x, -vfd->view_box.y);
}
efl_canvas_vg_node_transformation_set(root, &m);
}
void
evas_cache_vg_init(void)
{
@ -245,82 +317,26 @@ evas_cache_vg_file_open(const char *file, const char *key)
return vfd;
}
static void
_local_transformation(Efl_VG *root, double w, double h, Vg_File_Data *vfd)
Vg_Cache_Entry*
evas_cache_vg_entry_resize(Vg_Cache_Entry *vg_entry, int w, int h)
{
double sx = 0, sy= 0, scale;
Eina_Matrix3 m;
if (vfd->view_box.w)
sx = w/vfd->view_box.w;
if (vfd->view_box.h)
sy = h/vfd->view_box.h;
scale = sx < sy ? sx: sy;
eina_matrix3_identity(&m);
// allign hcenter and vcenter
if (vfd->preserve_aspect)
{
eina_matrix3_translate(&m, (w - vfd->view_box.w * scale)/2.0, (h - vfd->view_box.h * scale)/2.0);
eina_matrix3_scale(&m, scale, scale);
eina_matrix3_translate(&m, -vfd->view_box.x, -vfd->view_box.y);
}
else
{
eina_matrix3_scale(&m, sx, sy);
eina_matrix3_translate(&m, -vfd->view_box.x, -vfd->view_box.y);
}
efl_canvas_vg_node_transformation_set(root, &m);
}
static Efl_VG *
_evas_vg_dup_vg_tree(Vg_File_Data *vfd, double w, double h)
{
Efl_VG *root;
if (!vfd) return NULL;
if (w < 1 || h < 1) return NULL;
root = efl_duplicate(vfd->root);
_local_transformation(root, w, h, vfd);
return root;
}
static void
_evas_cache_vg_tree_update(Vg_Cache_Entry *vg_entry)
{
Vg_File_Data *vfd = NULL;
if(!vg_entry) return;
if (!vg_entry->file)
{
vg_entry->root = NULL;
return;
}
vfd = evas_cache_vg_file_open(vg_entry->file, vg_entry->key);
vg_entry->root = _evas_vg_dup_vg_tree(vfd, vg_entry->w, vg_entry->h);
eina_stringshare_del(vg_entry->file);
eina_stringshare_del(vg_entry->key);
vg_entry->file = NULL;
vg_entry->key = NULL;
return evas_cache_vg_entry_create(vg_entry->file, vg_entry->key, w, h);
}
Vg_Cache_Entry*
evas_cache_vg_entry_find(const char *file, const char *key,
int w, int h)
evas_cache_vg_entry_create(const char *file,
const char *key,
int w, int h)
{
Vg_Cache_Entry* vg_entry;
Eina_Strbuf *hash_key;
if (!vg_cache) return NULL;
//TODO: zero-sized entry is useless. how to skip it?
hash_key = eina_strbuf_new();
eina_strbuf_append_printf(hash_key, "%s/%s/%d/%d",
file, key, w, h);
eina_strbuf_append_printf(hash_key, "%s/%s/%d/%d", file, key, w, h);
vg_entry = eina_hash_find(vg_cache->vg_entry_hash, eina_strbuf_string_get(hash_key));
if (!vg_entry)
{
@ -340,16 +356,42 @@ evas_cache_vg_entry_find(const char *file, const char *key,
}
eina_strbuf_free(hash_key);
vg_entry->ref++;
vg_entry->vfd = evas_cache_vg_file_open(file, key);
//No File??
if (!vg_entry->vfd)
{
evas_cache_vg_entry_del(vg_entry);
return NULL;
}
vg_entry->vfd->ref++;
return vg_entry;
}
Efl_VG*
evas_cache_vg_tree_get(Vg_Cache_Entry *vg_entry)
{
if (vg_entry->root) return vg_entry->root;
if (!vg_entry) return NULL;
if ((vg_entry->w < 1) || (vg_entry->h < 1)) return NULL;
if (vg_entry->file)
_evas_cache_vg_tree_update(vg_entry);
Vg_File_Data *vfd = vg_entry->vfd;
if (!vfd) return NULL;
Efl_VG *root = _cached_root_get(vg_entry);
if (root) return root;
if (!vfd->static_viewbox)
{
vfd->view_box.w = vg_entry->w;
vfd->view_box.h = vg_entry->h;
}
if (!vfd->loader->file_data(vfd)) return NULL;
_caching_root_update(vg_entry);
_local_transform(vg_entry->root, vg_entry->w, vg_entry->h, vfd);
return vg_entry->root;
}
@ -357,10 +399,11 @@ evas_cache_vg_tree_get(Vg_Cache_Entry *vg_entry)
void
evas_cache_vg_entry_del(Vg_Cache_Entry *vg_entry)
{
if (!vg_entry) return;
if (!vg_cache || !vg_entry) return;
vg_entry->ref--;
// FIXME implement delete logic (LRU)
if (vg_entry->ref > 0) return;
if (!eina_hash_del(vg_cache->vg_entry_hash, vg_entry->hash_key, vg_entry))
ERR("Failed to delete vg_entry = (%p) from hash", vg_entry);
}
Eina_Bool

View File

@ -38,13 +38,26 @@ evas_vg_load_file_open_eet(const char *file, const char *key, int *error EINA_UN
{
*error = EVAS_LOAD_ERROR_NONE;
}
return vg_common_create_vg_node(node);
}
static Eina_Bool
evas_vg_load_file_close_eet(Vg_File_Data *vfd EINA_UNUSED)
{
return EINA_TRUE;
}
static Eina_Bool
evas_vg_load_file_data_eet(Vg_File_Data *vfd EINA_UNUSED)
{
return EINA_TRUE;
}
static Evas_Vg_Load_Func evas_vg_load_eet_func =
{
evas_vg_load_file_open_eet
evas_vg_load_file_open_eet,
evas_vg_load_file_close_eet,
evas_vg_load_file_data_eet
};
static int

View File

@ -2325,6 +2325,18 @@ _update_gradient(Svg_Node *node, Eina_List *grad_list)
}
}
}
static Eina_Bool
evas_vg_load_file_data_svg(Vg_File_Data *vfd EINA_UNUSED)
{
return EINA_TRUE;
}
static Eina_Bool
evas_vg_load_file_close_svg(Vg_File_Data *vfd)
{
if (vfd->root) efl_unref(vfd->root);
return EINA_TRUE;
}
static Vg_File_Data*
evas_vg_load_file_open_svg(const char *file, const char *key EINA_UNUSED, int *error EINA_UNUSED)
@ -2377,7 +2389,9 @@ evas_vg_load_file_open_svg(const char *file, const char *key EINA_UNUSED, int *e
static Evas_Vg_Load_Func evas_vg_load_svg_func =
{
evas_vg_load_file_open_svg
evas_vg_load_file_open_svg,
evas_vg_load_file_close_svg,
evas_vg_load_file_data_svg,
};
static int

View File

@ -832,6 +832,7 @@ vg_common_create_vg_node(Svg_Node *node)
vg_data->view_box.w = node->node.doc.vw;
vg_data->view_box.h = node->node.doc.vh;
vg_data->preserve_aspect = node->node.doc.preserve_aspect;
vg_data->static_viewbox = EINA_TRUE;
vg_data->root = vg_common_create_vg_node_helper(node, NULL, vg_data);
return vg_data;