efl: make Efl.Ui.Factory.release work in batches.

This will enable better strategy in scheduling removal of object from the cache
instead of doing the throttling in the View as there is a better understanding
of the different layer the items are going to go through and where they will
consume time.

Reviewed-by: Marcel Hollerbach <mail@marcel-hollerbach.de>
Differential Revision: https://phab.enlightenment.org/D9955
This commit is contained in:
Cedric Bail 2019-09-15 22:39:05 -07:00 committed by Marcel Hollerbach
parent 0a99fb87bb
commit 5493cc5396
5 changed files with 108 additions and 50 deletions

View File

@ -29,7 +29,7 @@ interface @beta Efl.Ui.Factory extends Efl.Ui.Property_Bind, Efl.Ui.Factory_Bind
release {
[[Release a UI object and disconnect from models.]]
params {
ui_view: Efl.Gfx.Entity; [[Object to remove.]]
ui_views: iterator<Efl.Gfx.Entity> @move; [[Object to remove.]]
}
}
}

View File

@ -21,6 +21,7 @@ struct _Efl_Ui_Caching_Factory_Data
// end of the list objects are added and removed.
Eina_List *cache;
Eina_Hash *lookup;
Eina_Future *flush;
struct {
unsigned int memory;
@ -57,15 +58,29 @@ _efl_ui_caching_factory_remove(Efl_Ui_Caching_Factory_Data *pd, Eina_List *l, Ef
static void
_efl_ui_caching_factory_item_del(Eo *obj, Efl_Ui_Caching_Factory_Data *pd,
Efl_Gfx_Entity *entity)
Eina_Iterator *entities)
{
if (pd->klass) efl_del(entity);
else efl_ui_factory_release(efl_super(obj, EFL_UI_CACHING_FACTORY_CLASS), entity);
if (!pd->klass)
{
efl_ui_factory_release(efl_super(obj, EFL_UI_CACHING_FACTORY_CLASS), entities);
}
else
{
Efl_Gfx_Entity *entity;
EINA_ITERATOR_FOREACH(entities, entity)
efl_del(entity);
eina_iterator_free(entities);
}
}
static void
_efl_ui_caching_factory_flush(Eo *obj, Efl_Ui_Caching_Factory_Data *pd)
{
Eina_Array scheduled;
eina_array_step_set(&scheduled, sizeof (Eina_Array), 8);
while (pd->limit.items != 0 &&
pd->current.items > pd->limit.items)
{
@ -75,7 +90,8 @@ _efl_ui_caching_factory_flush(Eo *obj, Efl_Ui_Caching_Factory_Data *pd)
_efl_ui_caching_factory_remove(pd, eina_list_last(pd->cache), entity);
if (pd->lookup) eina_hash_del(pd->lookup, efl_ui_widget_style_get(entity), entity);
_efl_ui_caching_factory_item_del(obj, pd, entity);
eina_array_push(&scheduled, entity);
}
while (pd->limit.memory != 0 &&
@ -87,8 +103,13 @@ _efl_ui_caching_factory_flush(Eo *obj, Efl_Ui_Caching_Factory_Data *pd)
_efl_ui_caching_factory_remove(pd, eina_list_last(pd->cache), entity);
if (pd->lookup) eina_hash_del(pd->lookup, efl_ui_widget_style_get(entity), entity);
_efl_ui_caching_factory_item_del(obj, pd, entity);
eina_array_push(&scheduled, entity);
}
// We could improve this by doing some limited batch to reduce potential spike usage
_efl_ui_caching_factory_item_del(obj, pd, eina_array_iterator_new(&scheduled));
eina_array_flush(&scheduled);
}
static Eina_Value
@ -343,38 +364,67 @@ _efl_ui_caching_factory_items_limit_get(const Eo *obj EINA_UNUSED,
return pd->limit.items;
}
static void
_efl_ui_caching_factory_efl_ui_factory_release(Eo *obj,
Efl_Ui_Caching_Factory_Data *pd,
Efl_Gfx_Entity *ui_view)
static Eina_Value
_schedule_cache_flush(Eo *obj, void *data, const Eina_Value v)
{
// Are we invalidated ?
if (pd->invalidated)
{
_efl_ui_caching_factory_item_del(obj, pd, ui_view);
return;
}
// Change parent, disconnect the object and make it invisible
efl_gfx_entity_visible_set(ui_view, EINA_FALSE);
efl_event_callback_call(obj, EFL_UI_FACTORY_EVENT_ITEM_RELEASING, ui_view);
// Add to the cache
pd->cache = eina_list_prepend(pd->cache, ui_view);
pd->current.items++;
pd->current.memory += efl_class_memory_size_get(ui_view);
if (efl_isa(ui_view, EFL_CACHED_ITEM_INTERFACE))
pd->current.memory += efl_cached_item_memory_size_get(ui_view);
// Fill lookup
if (!pd->klass && efl_ui_widget_style_get(ui_view))
{
if (!pd->lookup) pd->lookup = eina_hash_string_djb2_new(NULL);
eina_hash_direct_add(pd->lookup, efl_ui_widget_style_get(ui_view), ui_view);
}
Efl_Ui_Caching_Factory_Data *pd = data;
// And check if the cache need some triming
_efl_ui_caching_factory_flush(obj, pd);
return v;
}
static void
_schedule_done(Eo *o EINA_UNUSED, void *data, const Eina_Future *dead_future EINA_UNUSED)
{
Efl_Ui_Caching_Factory_Data *pd = data;
pd->flush = NULL;
}
static void
_efl_ui_caching_factory_efl_ui_factory_release(Eo *obj,
Efl_Ui_Caching_Factory_Data *pd,
Eina_Iterator *ui_views)
{
Efl_Gfx_Entity *ui_view;
// Are we invalidated ?
if (pd->invalidated)
{
_efl_ui_caching_factory_item_del(obj, pd, ui_views);
return;
}
EINA_ITERATOR_FOREACH(ui_views, ui_view)
{
// Change parent, disconnect the object and make it invisible
efl_gfx_entity_visible_set(ui_view, EINA_FALSE);
efl_event_callback_call(obj, EFL_UI_FACTORY_EVENT_ITEM_RELEASING, ui_view);
// Add to the cache
pd->cache = eina_list_prepend(pd->cache, ui_view);
pd->current.items++;
pd->current.memory += efl_class_memory_size_get(ui_view);
if (efl_isa(ui_view, EFL_CACHED_ITEM_INTERFACE))
pd->current.memory += efl_cached_item_memory_size_get(ui_view);
// Fill lookup
if (!pd->klass && efl_ui_widget_style_get(ui_view))
{
if (!pd->lookup) pd->lookup = eina_hash_string_djb2_new(NULL);
eina_hash_direct_add(pd->lookup, efl_ui_widget_style_get(ui_view), ui_view);
}
}
eina_iterator_free(ui_views);
// Schedule a cache flush if necessary
if (!pd->flush)
pd->flush = efl_future_then(obj, efl_loop_job(efl_loop_get(obj)),
.success = _schedule_cache_flush,
.free = _schedule_done,
.data = pd);
}
static void

View File

@ -2193,14 +2193,14 @@ _content_created(Eo *obj, void *data, const Eina_Value value)
{
Efl_Ui_Layout_Factory_Request *request = data;
Efl_Gfx_Entity *content = NULL;
Efl_Gfx_Entity *old_content;
Efl_Gfx_Entity *old_content[1];
int len, i;
EINA_VALUE_ARRAY_FOREACH(&value, len, i, content)
{
// Recycle old content
old_content = efl_content_get(efl_part(obj, request->key));
if (old_content) efl_ui_factory_release(request->factory, old_content);
old_content[0] = efl_content_get(efl_part(obj, request->key));
if (old_content[0]) efl_ui_factory_release(request->factory, EINA_C_ARRAY_ITERATOR_NEW(old_content));
// Set new content
efl_content_set(efl_part(obj, request->key), content);
@ -2364,7 +2364,7 @@ _efl_ui_layout_base_model_register(Eo *obj, Efl_Ui_Layout_Data *pd,
EINA_ITERATOR_FOREACH(it, tuple)
{
Efl_Ui_Layout_Factory_Tracking *factory;
Efl_Gfx_Entity *content;
Efl_Gfx_Entity *content[1];
key = tuple->key;
factory = tuple->data;
@ -2373,11 +2373,11 @@ _efl_ui_layout_base_model_register(Eo *obj, Efl_Ui_Layout_Data *pd,
if (factory->in_flight) eina_future_cancel(factory->in_flight);
// Cleanup content
content = efl_content_get(efl_part(obj, key));
content[0] = efl_content_get(efl_part(obj, key));
efl_content_unset(efl_part(obj, key));
// And recycle it
if (content) efl_ui_factory_release(factory->factory, content);
if (content[0]) efl_ui_factory_release(factory->factory, EINA_C_ARRAY_ITERATOR_NEW(content));
}
eina_iterator_free(it);
@ -2500,12 +2500,12 @@ _efl_ui_layout_base_efl_ui_factory_bind_factory_bind(Eo *obj EINA_UNUSED, Efl_Ui
tracking = eina_hash_find(pd->connect.factories, ss_key);
if (tracking)
{
Efl_Gfx_Entity *old;
Efl_Gfx_Entity *old[1];
// Unset and recycle
old = efl_content_get(efl_part(obj, ss_key));
old[0] = efl_content_get(efl_part(obj, ss_key));
efl_content_unset(efl_part(obj, ss_key));
if (old) efl_ui_factory_release(tracking->factory, old);
if (old[0]) efl_ui_factory_release(tracking->factory, EINA_C_ARRAY_ITERATOR_NEW(old));
// Stop in flight request
if (tracking->in_flight) eina_future_cancel(tracking->in_flight);

View File

@ -973,6 +973,7 @@ EOLIAN static void
_efl_ui_list_view_efl_ui_list_view_model_unrealize(Eo *obj, Efl_Ui_List_View_Data *pd, Efl_Ui_List_View_Layout_Item *item)
{
Efl_Ui_List_View_Item_Event evt;
Efl_Gfx_Entity *entities[1];
EINA_SAFETY_ON_NULL_RETURN(item);
if (!item->layout)
@ -1001,7 +1002,8 @@ _efl_ui_list_view_efl_ui_list_view_model_unrealize(Eo *obj, Efl_Ui_List_View_Dat
efl_event_callback_call(obj, EFL_UI_LIST_VIEW_EVENT_ITEM_UNREALIZED, &evt);
efl_canvas_group_member_remove(obj, pd->pan_obj);
efl_ui_factory_release(pd->factory, item->layout);
entities[0] = item->layout;
efl_ui_factory_release(pd->factory, EINA_C_ARRAY_ITERATOR_NEW(entities));
item->layout = NULL;
}

View File

@ -326,13 +326,19 @@ alloc_array_error:
static void
_efl_ui_widget_factory_efl_ui_factory_release(Eo *obj,
Efl_Ui_Widget_Factory_Data *pd EINA_UNUSED,
Efl_Gfx_Entity *ui_view)
Eina_Iterator *ui_views)
{
// There might be multiple call to releasing on the same object as every factory in the
// inheritance chain can decide to keep it for a time
efl_event_callback_call(obj, EFL_UI_FACTORY_EVENT_ITEM_RELEASING, ui_view);
// We do not cache or track this item, just get rid of them asap
efl_del(ui_view);
Efl_Gfx_Entity *ui_view;
EINA_ITERATOR_FOREACH(ui_views, ui_view)
{
// There might be multiple call to releasing on the same object as every factory in the
// inheritance chain can decide to keep it for a time
efl_event_callback_call(obj, EFL_UI_FACTORY_EVENT_ITEM_RELEASING, ui_view);
// We do not cache or track this item, just get rid of them asap
efl_del(ui_view);
}
eina_iterator_free(ui_views);
}
Eina_Stringshare *_property_style_ss = NULL;