diff --git a/src/lib/efl/interfaces/efl_ui_factory.eo b/src/lib/efl/interfaces/efl_ui_factory.eo index 1db7d8fb9c..47dd203f6e 100644 --- a/src/lib/efl/interfaces/efl_ui_factory.eo +++ b/src/lib/efl/interfaces/efl_ui_factory.eo @@ -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 @move; [[Object to remove.]] } } } diff --git a/src/lib/elementary/efl_ui_caching_factory.c b/src/lib/elementary/efl_ui_caching_factory.c index 54f5fd9442..7dbfc0aae9 100644 --- a/src/lib/elementary/efl_ui_caching_factory.c +++ b/src/lib/elementary/efl_ui_caching_factory.c @@ -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 diff --git a/src/lib/elementary/efl_ui_layout.c b/src/lib/elementary/efl_ui_layout.c index e0b8250f63..fd52af6f16 100644 --- a/src/lib/elementary/efl_ui_layout.c +++ b/src/lib/elementary/efl_ui_layout.c @@ -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); diff --git a/src/lib/elementary/efl_ui_list_view.c b/src/lib/elementary/efl_ui_list_view.c index 8329301579..cee52e1132 100644 --- a/src/lib/elementary/efl_ui_list_view.c +++ b/src/lib/elementary/efl_ui_list_view.c @@ -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; } diff --git a/src/lib/elementary/efl_ui_widget_factory.c b/src/lib/elementary/efl_ui_widget_factory.c index d7fc34ba64..0be3e302d2 100644 --- a/src/lib/elementary/efl_ui_widget_factory.c +++ b/src/lib/elementary/efl_ui_widget_factory.c @@ -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;