Evas filters: Fix proxy usage (source unset)

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.
This commit is contained in:
Jean-Philippe Andre 2014-02-05 20:09:05 +09:00
parent a25b212bac
commit 9d821a402a
6 changed files with 118 additions and 126 deletions

View 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);

View 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?");
@ -2729,42 +2732,92 @@ _filter_program_set(Eo *eo_obj, void *_pd, va_list *list)
evas_object_inform_call_resize(eo_obj);
}
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);

View 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.

View 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);
}
}

View 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)

View 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