Evas filters: Fix proxy usage (source unset)
authorJean-Philippe Andre <jp.andre@samsung.com>
Wed, 5 Feb 2014 11:09:05 +0000 (20:09 +0900)
committerJean-Philippe Andre <jp.andre@samsung.com>
Fri, 7 Feb 2014 08:33:18 +0000 (17:33 +0900)
Proxy sources & objects were not properly unset.
This results either in crashes (especially in the Edje tests)
or dangling objects with tons of references.

Remove the refcount increase/decrease, as it is redundant.
Store pairs proxy+source instead of just the source in all hashes,
so we can unset the is_proxy flag on the proxy when there are no
sources anymore.

src/lib/evas/canvas/evas_object_main.c
src/lib/evas/canvas/evas_object_text.c
src/lib/evas/filters/evas_filter.c
src/lib/evas/filters/evas_filter_parser.c
src/lib/evas/filters/evas_filter_private.h
src/lib/evas/include/evas_filter.h

index 642a081..edef53b 100644 (file)
@@ -622,6 +622,8 @@ _destructor(Eo *eo_obj, void *_pd, va_list *list EINA_UNUSED)
    MAGIC_CHECK_END();
    Evas_Object_Protected_Data *obj = _pd;
    Evas_Object_Protected_Data *tmp;
+   Evas_Object *proxy;
+   Eina_List *l, *l2;
 
    evas_object_hide(eo_obj);
    if (obj->focused)
@@ -654,8 +656,8 @@ _destructor(Eo *eo_obj, void *_pd, va_list *list EINA_UNUSED)
    evas_object_grabs_cleanup(eo_obj, obj);
    EINA_LIST_FREE(obj->clip.clipees, tmp)
      evas_object_clip_unset(tmp->object);
-   while (obj->proxy->proxies)
-     evas_object_image_source_unset(obj->proxy->proxies->data);
+   EINA_LIST_FOREACH_SAFE(obj->proxy->proxies, l, l2, proxy)
+     evas_object_image_source_unset(proxy);
    if (obj->cur->clipper) evas_object_clip_unset(eo_obj);
    evas_object_map_set(eo_obj, NULL);
    if (obj->is_smart) evas_object_smart_del(eo_obj);
index cecc402..9b58a69 100644 (file)
@@ -49,7 +49,8 @@ struct _Evas_Object_Text
       struct {
          Eina_Stringshare    *code;
          Evas_Filter_Program *chain;
-         Eina_Hash           *sources;
+         Eina_Hash           *sources; // Evas_Filter_Proxy_Binding
+         int                  sources_count;
          void                *output;
          Eina_Bool            changed : 1;
       } filter;
@@ -1977,13 +1978,15 @@ evas_object_text_free(Evas_Object *eo_obj, Evas_Object_Protected_Data *obj)
 
    /* free filter output */
    if (o->cur.filter.output)
-     {
-        ENFN->image_free(ENDT, o->cur.filter.output);
-        o->cur.filter.output = NULL;
-     }
+     ENFN->image_free(ENDT, o->cur.filter.output);
+   eina_hash_free(o->cur.filter.sources);
    evas_filter_program_del(o->cur.filter.chain);
    eina_stringshare_del(o->cur.filter.code);
+   o->cur.filter.output = NULL;
    o->cur.filter.chain = NULL;
+   o->cur.filter.sources = NULL;
+   o->cur.filter.code = NULL;
+   o->cur.filter.sources_count = 0;
 
    /* free obj */
    _evas_object_text_items_clear(o);
@@ -2156,14 +2159,14 @@ evas_object_text_render(Evas_Object *eo_obj EINA_UNUSED,
              // Scan proxies to find if any changed
              if (!redraw && o->cur.filter.sources)
                {
+                  Evas_Filter_Proxy_Binding *pb;
                   Evas_Object_Protected_Data *source;
-                  Evas_Object *eo_source;
                   Eina_Iterator *it;
 
                   it = eina_hash_iterator_data_new(o->cur.filter.sources);
-                  EINA_ITERATOR_FOREACH(it, eo_source)
+                  EINA_ITERATOR_FOREACH(it, pb)
                     {
-                       source = eo_data_scope_get(eo_source, EVAS_OBJ_CLASS);
+                       source = eo_data_scope_get(pb->eo_source, EVAS_OBJ_CLASS);
                        if (source->changed)
                          {
                             redraw = EINA_TRUE;
@@ -2187,7 +2190,7 @@ evas_object_text_render(Evas_Object *eo_obj EINA_UNUSED,
           }
 
         filter = evas_filter_context_new(obj->layer->evas, do_async);
-        ok = evas_filter_context_program_use(filter, eo_obj, o->cur.filter.chain);
+        ok = evas_filter_context_program_use(filter, o->cur.filter.chain);
         if (!filter || !ok)
           {
              ERR("Parsing failed?");
@@ -2730,41 +2733,91 @@ _filter_program_set(Eo *eo_obj, void *_pd, va_list *list)
 }
 
 static void
+_filter_source_hash_free_cb(void *data)
+{
+   Evas_Filter_Proxy_Binding *pb = data;
+   Evas_Object_Protected_Data *proxy, *source;
+   Evas_Object_Text *o;
+
+   proxy = eo_data_scope_get(pb->eo_proxy, EVAS_OBJ_CLASS);
+   source = eo_data_scope_get(pb->eo_source, EVAS_OBJ_CLASS);
+
+   if (source)
+     {
+        EINA_COW_WRITE_BEGIN(evas_object_proxy_cow, source->proxy,
+                             Evas_Object_Proxy_Data, source_write)
+          source_write->proxies = eina_list_remove(source_write->proxies, pb->eo_proxy);
+        EINA_COW_WRITE_END(evas_object_proxy_cow, source->proxy, source_write)
+     }
+
+   o = eo_data_scope_get(pb->eo_proxy, MY_CLASS);
+
+   if (o && proxy)
+     {
+        o->cur.filter.sources_count--;
+        if (!o->cur.filter.sources_count)
+          {
+             EINA_COW_WRITE_BEGIN(evas_object_proxy_cow, proxy->proxy,
+                                  Evas_Object_Proxy_Data, proxy_write)
+               proxy_write->is_proxy = EINA_FALSE;
+             EINA_COW_WRITE_END(evas_object_proxy_cow, source->proxy, proxy_write)
+          }
+     }
+
+   eina_stringshare_del(pb->name);
+   free(pb);
+}
+
+static void
 _filter_source_set(Eo *eo_obj, void *_pd, va_list *list)
 {
    Evas_Object_Text *o = _pd;
    Evas_Object_Protected_Data *obj;
-   Evas_Filter_Program *pgm = NULL;
+   Evas_Filter_Program *pgm = o->cur.filter.chain;
    const char *name = va_arg(list, const char *);
-   Evas_Object *proxy = va_arg(list, Evas_Object *);
+   Evas_Object *eo_source = va_arg(list, Evas_Object *);
+   Evas_Filter_Proxy_Binding *pb, *pb_old = NULL;
+   Evas_Object_Protected_Data *source;
 
-   pgm = o->cur.filter.chain;
-   if (!pgm)
+   if (!o->cur.filter.sources)
      {
-        Evas_Object *old;
-        if (!proxy) return;
-        if (!o->cur.filter.sources)
-          {
-             o->cur.filter.sources = eina_hash_string_small_new
-                   (EINA_FREE_CB(evas_object_unref));
-          }
-        else
-          {
-             old = eina_hash_find(o->cur.filter.sources, name);
-             if (old == proxy) return;
-             if (old) eina_hash_del(o->cur.filter.sources, name, old);
-          }
-        evas_object_ref(proxy);
-        eina_hash_add(o->cur.filter.sources, name, proxy);
-        o->cur.filter.changed = EINA_TRUE;
-        return;
+        o->cur.filter.sources = eina_hash_string_small_new
+              (EINA_FREE_CB(_filter_source_hash_free_cb));
+     }
+   else
+     {
+        pb_old = eina_hash_find(o->cur.filter.sources, name);
+        if (pb_old && (pb_old->eo_source == eo_source)) return;
+        eina_hash_del(o->cur.filter.sources, name, pb_old);
      }
 
-   evas_filter_program_source_set(pgm, name, proxy);
-   o->cur.filter.changed = EINA_TRUE;
+   obj = eo_data_scope_get(eo_obj, EVAS_OBJ_CLASS);
+   source = eo_data_scope_get(eo_source, EVAS_OBJ_CLASS);
+   if (!source) return;
+
+   pb = calloc(1, sizeof(*pb));
+   pb->eo_proxy = eo_obj;
+   pb->eo_source = eo_source;
+   pb->name = eina_stringshare_add(name);
+
+   EINA_COW_WRITE_BEGIN(evas_object_proxy_cow, source->proxy,
+                        Evas_Object_Proxy_Data, source_write)
+     if (!eina_list_data_find(source_write->proxies, eo_obj))
+       source_write->proxies = eina_list_append(source_write->proxies, eo_obj);
+   EINA_COW_WRITE_END(evas_object_proxy_cow, source->proxy, source_write)
+
+   EINA_COW_WRITE_BEGIN(evas_object_proxy_cow, obj->proxy,
+                        Evas_Object_Proxy_Data, proxy_write)
+     proxy_write->is_proxy = EINA_TRUE;
+   EINA_COW_WRITE_END(evas_object_proxy_cow, obj->proxy, proxy_write)
+
+   eina_hash_add(o->cur.filter.sources, pb->name, pb);
+   o->cur.filter.sources_count++;
+
+   evas_filter_program_source_set_all(pgm, o->cur.filter.sources);
 
    // Update object
-   obj = eo_data_scope_get(eo_obj, EVAS_OBJ_CLASS);
+   o->cur.filter.changed = EINA_TRUE;
    _evas_object_text_items_clear(o);
    o->changed = 1;
    _evas_object_text_recalc(eo_obj, o->cur.text);
index 0811bd6..0c8a6a3 100644 (file)
@@ -120,41 +120,6 @@ _filter_buffer_backing_free(Evas_Filter_Buffer *fb)
    _backing_free(fb->ctx, backing);
 }
 
-/** @hidden private bind proxy to context */
-void
-evas_filter_context_source_set(Evas_Filter_Context *ctx, Evas_Object *eo_proxy,
-                               Evas_Object *eo_source, int bufid,
-                               Eina_Stringshare *name)
-{
-   Evas_Object_Protected_Data *proxy = eo_data_scope_get(eo_proxy, EVAS_OBJ_CLASS);
-   Evas_Object_Protected_Data *source = eo_data_scope_get(eo_source, EVAS_OBJ_CLASS);
-   Evas_Filter_Buffer *fb;
-
-   fb = _filter_buffer_get(ctx, bufid);
-   EINA_SAFETY_ON_NULL_RETURN(fb);
-
-   if (fb->source == eo_source) return;
-   evas_object_unref(fb->source);
-   eina_stringshare_del(fb->source_name);
-   fb->source = eo_source;
-   if (!fb->source) return;
-   fb->source_name = eina_stringshare_ref(name);
-
-   evas_object_ref(eo_source);
-
-   EINA_COW_WRITE_BEGIN(evas_object_proxy_cow, proxy->proxy, Evas_Object_Proxy_Data, proxy_write)
-     proxy_write->is_proxy = EINA_TRUE;
-   EINA_COW_WRITE_END(evas_object_proxy_cow, proxy->proxy, proxy_write);
-
-   EINA_COW_WRITE_BEGIN(evas_object_proxy_cow, source->proxy, Evas_Object_Proxy_Data, proxy_src_write)
-     {
-        if (!eina_list_data_find(source->proxy->proxies, eo_proxy))
-          proxy_src_write->proxies = eina_list_append(proxy_src_write->proxies, eo_proxy);
-        proxy_src_write->redraw = EINA_TRUE;
-     }
-   EINA_COW_WRITE_END(evas_object_proxy_cow, source->proxy, proxy_src_write);
-}
-
 
 /**
  * Render the source object when a proxy is set.
index b361e37..1b41a9b 100644 (file)
@@ -103,7 +103,7 @@ struct _Evas_Filter_Instruction
 struct _Evas_Filter_Program
 {
    Eina_Stringshare *name; // Optional for now
-   Eina_Hash /* const char * : Evas_Object */ *proxies;
+   Eina_Hash /* const char * : Evas_Filter_Proxy_Binding */ *proxies;
    Eina_Inlist /* Evas_Filter_Instruction */ *instructions;
    Eina_Inlist /* Buffer */ *buffers;
    Eina_Bool valid : 1;
@@ -1181,7 +1181,6 @@ evas_filter_program_del(Evas_Filter_Program *pgm)
      }
 
    eina_stringshare_del(pgm->name);
-   eina_hash_free(pgm->proxies);
    free(pgm);
 }
 
@@ -1375,64 +1374,19 @@ evas_filter_program_new(const char *name)
    pgm = calloc(1, sizeof(Evas_Filter_Program));
    if (!pgm) return NULL;
    pgm->name = eina_stringshare_add(name);
-   pgm->proxies = eina_hash_string_small_new(EINA_FREE_CB(evas_object_unref));
    _buffer_add(pgm, "input", EINA_TRUE, NULL);
    _buffer_add(pgm, "output", EINA_FALSE, NULL);
 
    return pgm;
 }
 
-/** Bind an object for proxy rendering */
-
-void
-evas_filter_program_source_set(Evas_Filter_Program *pgm,
-                               const char *name, Evas_Object *object)
-{
-   Evas_Object *old;
-
-   old = eina_hash_find(pgm->proxies, name);
-   if (old == object) return;
-   if (old) eina_hash_del(pgm->proxies, name, old);
-
-   evas_object_ref(object);
-   eina_hash_add(pgm->proxies, name, object);
-}
-
+/** Bind objects for proxy rendering */
 void
 evas_filter_program_source_set_all(Evas_Filter_Program *pgm,
                                    Eina_Hash *proxies)
 {
-   Eina_Hash_Tuple *tuple;
-   Eina_Iterator *it;
-   Evas_Object *old;
-
-   if (!pgm || !proxies) return;
-
-   it = eina_hash_iterator_tuple_new(proxies);
-   EINA_ITERATOR_FOREACH(it, tuple)
-     {
-        Eina_Stringshare *name = tuple->key;
-        Eo *source = tuple->data;
-
-        old = eina_hash_find(pgm->proxies, name);
-        if (old)
-          {
-             INF("Buffer %s already exists, skipping proxy source.", name);
-             continue;
-          }
-
-        INF("Binding object %p as '%s'", source, name);
-        evas_filter_program_source_set(pgm, name, source);
-     }
-   eina_iterator_free(it);
-}
-
-/** Get object used for proxy rendering */
-
-Evas_Object *
-evas_filter_program_source_get(Evas_Filter_Program *pgm, const char *name)
-{
-   return (Evas_Object *) eina_hash_find(pgm->proxies, name);
+   if (!pgm) return;
+   pgm->proxies = proxies;
 }
 
 /** Glue with Evas' filters */
@@ -1851,7 +1805,7 @@ _command_from_instruction(Evas_Filter_Context *ctx, Evas_Filter_Program *pgm,
 }
 
 Eina_Bool
-evas_filter_context_program_use(Evas_Filter_Context *ctx, Evas_Object *eo_obj,
+evas_filter_context_program_use(Evas_Filter_Context *ctx,
                                 Evas_Filter_Program *pgm)
 {
    Buffer *buf;
@@ -1873,8 +1827,16 @@ evas_filter_context_program_use(Evas_Filter_Context *ctx, Evas_Object *eo_obj,
         buf->cid = evas_filter_buffer_empty_new(ctx, buf->alpha);
         if (buf->proxy)
           {
-             Eo *eo_source = evas_filter_program_source_get(pgm, buf->proxy);
-             evas_filter_context_source_set(ctx, eo_obj, eo_source, buf->cid, buf->proxy);
+             Evas_Filter_Proxy_Binding *pb;
+             Evas_Filter_Buffer *fb;
+
+             pb = eina_hash_find(pgm->proxies, buf->proxy);
+             if (!pb) continue;
+
+             fb = _filter_buffer_get(ctx, buf->cid);
+             fb->proxy = pb->eo_proxy;
+             fb->source = pb->eo_source;
+             fb->source_name = eina_stringshare_ref(pb->name);
           }
      }
 
index b3c0539..eeacd42 100644 (file)
@@ -152,6 +152,8 @@ struct _Evas_Filter_Buffer
    void *glimage;
    int w, h;
 
+   Evas_Object *proxy;
+
    Eina_Bool alpha_only : 1;  // 1 channel (A) instead of 4 (RGBA)
    Eina_Bool allocated : 1;   // allocated on demand, belongs to this context
    Eina_Bool transient : 1;   // temporary buffer (automatic allocation)
index 453cd53..f569b6e 100644 (file)
@@ -9,6 +9,7 @@ typedef struct _Evas_Filter_Command Evas_Filter_Command;
 typedef struct _Evas_Filter_Program Evas_Filter_Program;
 typedef struct _Evas_Filter_Instruction Evas_Filter_Instruction;
 typedef struct _Evas_Filter_Buffer Evas_Filter_Buffer;
+typedef struct _Evas_Filter_Proxy_Binding Evas_Filter_Proxy_Binding;
 typedef enum _Evas_Filter_Mode Evas_Filter_Mode;
 typedef enum _Evas_Filter_Blur_Type Evas_Filter_Blur_Type;
 typedef enum _Evas_Filter_Channel Evas_Filter_Channel;
@@ -93,11 +94,10 @@ enum _Evas_Filter_Transform_Flags
 Evas_Filter_Program     *evas_filter_program_new(const char *name);
 Eina_Bool                evas_filter_program_parse(Evas_Filter_Program *pgm, const char *str);
 void                     evas_filter_program_del(Evas_Filter_Program *pgm);
-Eina_Bool                evas_filter_context_program_use(Evas_Filter_Context *ctx, Evas_Object *eo_obj, Evas_Filter_Program *pgm);
+Eina_Bool                evas_filter_context_program_use(Evas_Filter_Context *ctx, Evas_Filter_Program *pgm);
 Eina_Bool                evas_filter_program_padding_get(Evas_Filter_Program *pgm, int *l, int *r, int *t, int *b);
-void                     evas_filter_program_source_set(Evas_Filter_Program *pgm, const char *name, Evas_Object *object);
+//void                     evas_filter_program_source_set(Evas_Filter_Program *pgm, const char *name, Evas_Object *object);
 void                     evas_filter_program_source_set_all(Evas_Filter_Program *pgm, Eina_Hash *sources);
-Evas_Object             *evas_filter_program_source_get(Evas_Filter_Program *pgm, const char *name);
 void                     evas_filter_context_proxy_render_all(Evas_Filter_Context *ctx, Eo *eo_obj, Eina_Bool do_async);
 
 /* Filter context (low level) */
@@ -240,5 +240,13 @@ int                      evas_filter_command_bump_map_add(Evas_Filter_Context *c
  */
 int                      evas_filter_command_transform_add(Evas_Filter_Context *ctx, void *draw_context, int inbuf, int outbuf, Evas_Filter_Transform_Flags flags, int ox, int oy);
 
+/* Simple binding between a filter object and its sources */
+struct _Evas_Filter_Proxy_Binding
+{
+   Evas_Object *eo_proxy;
+   Evas_Object *eo_source;
+   Eina_Stringshare *name;
+};
+
 #endif