raster pushed a commit to branch master.

http://git.enlightenment.org/core/enlightenment.git/commit/?id=57ce6419e5c257e6fee6809cdb9c63d39c0b0a98

commit 57ce6419e5c257e6fee6809cdb9c63d39c0b0a98
Author: Carsten Haitzler (Rasterman) <ras...@rasterman.com>
Date:   Fri Apr 1 20:29:04 2016 +0900

    e icons: reduce mem usage (in x11) by a fair bit by de-duplicating
    
    so i was profiling today .. leak hunting .. and i noticed. if you have
    enough appss open - eg terminology, e uses a huge amount of memory...
    for icons. terminology is 128x128 ...  thats 64k per icon. open up a
    lot of terminology windows and we duplicate that 64k per every window
    on the wm sside because we get the data. it would apply for any app
    that sets a netwm icon. this can be come rather silly if you have like
    100 terminals. it's worse with larger icons (eg 256x256 - 256k per
    icon).
    
    this puts in a simply list for shared icons and a lookup on fetch to
    de-duplicate and share icon data. this should drop memory usage
    nicely.
    
    @improvement
---
 src/bin/e_client.c | 103 ++++++++++++++++++++++++++++++++++++++++++++++++++---
 src/bin/e_client.h |   2 ++
 src/bin/e_comp_x.c |  22 +++---------
 3 files changed, 105 insertions(+), 22 deletions(-)

diff --git a/src/bin/e_client.c b/src/bin/e_client.c
index 1d24366..e2a2bf6 100644
--- a/src/bin/e_client.c
+++ b/src/bin/e_client.c
@@ -534,10 +534,9 @@ _e_client_free(E_Client *ec)
    ec->stick_desks = eina_list_free(ec->stick_desks);
    if (ec->netwm.icons)
      {
-        int i;
-        for (i = 0; i < ec->netwm.num_icons; i++)
-          free(ec->netwm.icons[i].data);
-        E_FREE(ec->netwm.icons);
+        e_client_icon_free(ec->netwm.icons, ec->netwm.num_icons);
+        ec->netwm.icons = NULL;
+        ec->netwm.num_icons = 0;
      }
    E_FREE(ec->netwm.extra_types);
    eina_stringshare_replace(&ec->border.name, NULL);
@@ -5023,3 +5022,99 @@ e_client_layout_cb_set(E_Client_Layout_Cb cb)
      CRI("ATTEMPTING TO OVERWRITE EXISTING CLIENT LAYOUT HOOK!!!");
    _e_client_layout_cb = cb;
 }
+
+////////////////////////////////////////////
+static Eina_List *iconshare = NULL;
+
+typedef struct _E_Client_Icon_Entry E_Client_Icon_Entry;
+
+struct _E_Client_Icon_Entry
+{
+   Ecore_X_Icon *icons;
+   int num_icons;
+   int ref;
+};
+
+E_API Ecore_X_Icon *
+e_client_icon_deduplicate(Ecore_X_Icon *icons, int num_icons)
+{
+   int i;
+   Eina_List *l;
+   E_Client_Icon_Entry *ie;
+
+   // unless the rest of e uses border icons OTHER than icon #0
+   // then free the rest that we don't need anymore.
+   for (i = 1; i < num_icons; i++)
+     {
+        free(icons[i].data);
+        icons[i].data = NULL;
+     }
+   // lookup icon data in icons cache/share
+   EINA_LIST_FOREACH(iconshare, l, ie)
+     {
+        if ((ie->num_icons == num_icons) &&
+            (num_icons  > 0) &&
+            (ie->icons[0].width == icons[0].width) &&
+            (ie->icons[0].height == icons[0].height) &&
+            (!memcmp(ie->icons[0].data, icons[0].data,
+                     icons[0].width * icons[0].height * 4)))
+          {
+             // found so free the input icons
+             for (i = 0; i < num_icons; i++)
+               free(icons[i].data);
+             free(icons);
+             // ref the shared/cached one
+             ie->ref++;
+             iconshare = eina_list_promote_list(iconshare, l);
+             // and return that
+             return ie->icons;
+          }
+     }
+   // no hit - new entry to cache. add it
+   ie = calloc(1, sizeof(E_Client_Icon_Entry));
+   if (ie)
+     {
+        ie->icons = icons;
+        ie->num_icons = num_icons;
+        ie->ref = 1;
+        iconshare = eina_list_prepend(iconshare, ie);
+     }
+   return icons;
+}
+
+E_API void
+e_client_icon_free(Ecore_X_Icon *icons, int num_icons)
+{
+   int i;
+   Eina_List *l;
+   E_Client_Icon_Entry *ie;
+
+   // lookup in icon share cache
+   EINA_LIST_FOREACH(iconshare, l, ie)
+     {
+        if ((ie->num_icons == num_icons) &&
+            (num_icons  > 0) &&
+            (ie->icons[0].width == icons[0].width) &&
+            (ie->icons[0].height == icons[0].height) &&
+            (!memcmp(ie->icons[0].data, icons[0].data,
+                     icons[0].width * icons[0].height * 4)))
+          {
+             // found so deref
+             ie->ref--;
+             if (ie->ref <= 0)
+               {
+                  // no refs left - free the icon from the share/cache
+                  iconshare = eina_list_remove_list(iconshare, l);
+                  for (i = 0; i < ie->num_icons; i++)
+                    free(ie->icons[i].data);
+                  free(ie->icons);
+                  free(ie);
+               }
+             return;
+          }
+     }
+   // not found - so just free it ... odd - we should never be here
+   for (i = 0; i < num_icons; i++)
+     free(icons[i].data);
+   free(icons);
+}
diff --git a/src/bin/e_client.h b/src/bin/e_client.h
index 13d24fd..94ef059 100644
--- a/src/bin/e_client.h
+++ b/src/bin/e_client.h
@@ -827,6 +827,8 @@ E_API Eina_Bool e_client_has_xwindow(const E_Client *ec);
 E_API Eina_Bool e_client_desk_window_profile_available_check(E_Client *ec, 
const char *profile);
 E_API void      e_client_desk_window_profile_wait_desk_set(E_Client *ec, 
E_Desk *desk);
 E_API void      e_client_layout_cb_set(E_Client_Layout_Cb cb);
+E_API Ecore_X_Icon *e_client_icon_deduplicate(Ecore_X_Icon *icons, int 
num_icons);
+E_API void      e_client_icon_free(Ecore_X_Icon *icons, int num_icons);
 
 YOLO E_API void e_client_focus_stack_set(Eina_List *l);
 
diff --git a/src/bin/e_comp_x.c b/src/bin/e_comp_x.c
index 35831a8..2bff110 100644
--- a/src/bin/e_comp_x.c
+++ b/src/bin/e_comp_x.c
@@ -3795,30 +3795,16 @@ _e_comp_x_hook_client_fetch(void *d EINA_UNUSED, 
E_Client *ec)
      }
    if (ec->netwm.fetch.icon)
      {
-        int i;
-        if (ec->netwm.icons)
-          {
-             for (i = 0; i < ec->netwm.num_icons; i++)
-               {
-                  free(ec->netwm.icons[i].data);
-                  ec->netwm.icons[i].data = NULL;
-               }
-             free(ec->netwm.icons);
-          }
+        e_client_icon_free(ec->netwm.icons, ec->netwm.num_icons);
         ec->netwm.icons = NULL;
         ec->netwm.num_icons = 0;
         if (ecore_x_netwm_icons_get(win,
                                     &ec->netwm.icons,
                                     &ec->netwm.num_icons))
           {
-             // unless the rest of e uses border icons OTHER than icon #0
-             // then free the rest that we don't need anymore.
-             for (i = 1; i < ec->netwm.num_icons; i++)
-               {
-                  free(ec->netwm.icons[i].data);
-                  ec->netwm.icons[i].data = NULL;
-               }
-             ec->netwm.num_icons = 1;
+             if (ec->netwm.icons)
+               ec->netwm.icons = e_client_icon_deduplicate
+                 (ec->netwm.icons, ec->netwm.num_icons);
              ec->changes.icon = 1;
           }
         ec->netwm.fetch.icon = 0;

-- 


Reply via email to