evas filters: Fix async RW rendering

This fixes a crash (when deleting the output image), and rearranges
the code a bit.
This commit is contained in:
Jean-Philippe Andre 2017-04-13 12:08:31 +09:00
parent 30ac315631
commit 3c92b32c13
5 changed files with 226 additions and 262 deletions

View File

@ -120,14 +120,9 @@ _filter_end_sync(Evas_Filter_Context *ctx, Evas_Object_Protected_Data *obj,
FCOW_WRITE(pd, output, output);
}
if (EINA_UNLIKELY(ctx != pd->data->context))
{
ERR("Filter context has changed! Destroying it now...");
evas_filter_context_destroy(pd->data->context);
destroy = EINA_TRUE;
}
if (previous)
ENFN->image_free(ENDT, previous);
evas_filter_buffer_backing_release(ctx, previous);
if (destroy)
{
evas_filter_context_destroy(ctx);
@ -143,7 +138,7 @@ _filter_async_post_render_cb(void *data)
Evas_Filter_Post_Render_Data *task = data;
Evas_Filter_Data *pd = task->pd;
#ifdef DEBUG
#ifdef FILTERS_DEBUG
EINA_SAFETY_ON_FALSE_RETURN(eina_main_loop_is());
#endif
@ -158,12 +153,8 @@ _filter_cb(Evas_Filter_Context *ctx, void *data, Eina_Bool success)
Evas_Object_Protected_Data *obj;
Evas_Filter_Data *pd = data;
#ifdef DEBUG
EINA_SAFETY_ON_FALSE_RETURN(!eina_main_loop_is());
#endif
obj = pd->data->obj;
EINA_SAFETY_ON_FALSE_RETURN(obj && obj->layer && obj->layer->evas);
EVAS_OBJECT_DATA_VALID_CHECK(obj);
if (!pd->data->async)
{
@ -171,6 +162,10 @@ _filter_cb(Evas_Filter_Context *ctx, void *data, Eina_Bool success)
return;
}
#ifdef FILTERS_DEBUG
EINA_SAFETY_ON_FALSE_RETURN(!eina_main_loop_is());
#endif
post_data = calloc(1, sizeof(*post_data));
post_data->success = success;
post_data->ctx = ctx;
@ -252,234 +247,220 @@ evas_filter_object_render(Eo *eo_obj, Evas_Object_Protected_Data *obj,
int x, int y, Eina_Bool do_async, Eina_Bool alpha)
{
Evas_Filter_Data *pd = efl_data_scope_get(eo_obj, MY_CLASS);
int X, Y, W, H;
Evas_Filter_Context *filter;
void *drawctx;
Eina_Bool ok;
void *previous = pd->data->output;
Evas_Object_Filter_Data *fcow;
Eina_Bool use_map = EINA_FALSE;
Evas_Filter_Padding pad;
if (!pd->data->invalid && (pd->data->chain || pd->data->code))
if (pd->data->invalid || (!pd->data->chain && !pd->data->code))
return EINA_FALSE;
W = obj->cur->geometry.w;
H = obj->cur->geometry.h;
X = obj->cur->geometry.x;
Y = obj->cur->geometry.y;
// Prepare color multiplier
ENFN->context_color_set(output, context,
obj->cur->cache.clip.r,
obj->cur->cache.clip.g,
obj->cur->cache.clip.b,
obj->cur->cache.clip.a);
if (obj->cur->clipper)
ENFN->context_multiplier_set(output, context,
obj->cur->clipper->cur->cache.clip.r,
obj->cur->clipper->cur->cache.clip.g,
obj->cur->clipper->cur->cache.clip.b,
obj->cur->clipper->cur->cache.clip.a);
else
ENFN->context_multiplier_unset(output, context);
if (obj->map->cur.usemap && obj->map->cur.map && (obj->map->cur.map->count >= 4))
{
int X, Y, W, H;
Evas_Filter_Context *filter;
void *drawctx;
Eina_Bool ok;
void *previous = pd->data->output;
Evas_Object_Filter_Data *fcow;
Eina_Bool use_map = EINA_FALSE;
Evas_Filter_Padding pad;
int iw, ih;
W = obj->cur->geometry.w;
H = obj->cur->geometry.h;
X = obj->cur->geometry.x;
Y = obj->cur->geometry.y;
use_map = EINA_TRUE;
ENFN->image_size_get(ENDT, previous, &iw, &ih);
evas_object_map_update(eo_obj, x, y, iw, ih, iw, ih);
}
// Prepare color multiplier
ENFN->context_color_set(output, context,
obj->cur->cache.clip.r,
obj->cur->cache.clip.g,
obj->cur->cache.clip.b,
obj->cur->cache.clip.a);
if (obj->cur->clipper)
ENFN->context_multiplier_set(output, context,
obj->cur->clipper->cur->cache.clip.r,
obj->cur->clipper->cur->cache.clip.g,
obj->cur->clipper->cur->cache.clip.b,
obj->cur->clipper->cur->cache.clip.a);
else
ENFN->context_multiplier_unset(output, context);
if (!pd->data->chain)
{
Evas_Filter_Program *pgm;
Eina_Bool invalid;
if (obj->map->cur.usemap && obj->map->cur.map && (obj->map->cur.map->count >= 4))
pgm = evas_filter_program_new(pd->data->name, alpha);
evas_filter_program_source_set_all(pgm, pd->data->sources);
evas_filter_program_data_set_all(pgm, pd->data->data);
_evas_filter_state_set_internal(pgm, pd);
invalid = !evas_filter_program_parse(pgm, pd->data->code);
if (invalid)
{
int iw, ih;
use_map = EINA_TRUE;
ENFN->image_size_get(ENDT, previous, &iw, &ih);
evas_object_map_update(eo_obj, x, y, iw, ih, iw, ih);
ERR("Filter program parsing failed");
evas_filter_program_del(pgm);
pgm = NULL;
}
if (!pd->data->chain)
{
Evas_Filter_Program *pgm;
Eina_Bool invalid;
pgm = evas_filter_program_new(pd->data->name, alpha);
evas_filter_program_source_set_all(pgm, pd->data->sources);
evas_filter_program_data_set_all(pgm, pd->data->data);
_evas_filter_state_set_internal(pgm, pd);
invalid = !evas_filter_program_parse(pgm, pd->data->code);
if (invalid)
{
ERR("Filter program parsing failed");
evas_filter_program_del(pgm);
pgm = NULL;
}
fcow = FCOW_BEGIN(pd);
if (!invalid) evas_filter_program_padding_get(pgm, NULL, &fcow->padding);
fcow->chain = pgm;
fcow->invalid = invalid;
FCOW_END(fcow, pd);
if (invalid) return EINA_FALSE;
}
else if (previous && !pd->data->changed)
{
Eina_Bool redraw = EINA_TRUE;
if (_evas_filter_state_set_internal(pd->data->chain, pd))
DBG("Filter redraw by state change!");
else if (obj->changed)
DBG("Filter redraw by object content change!");
else if (obj->snapshot_needs_redraw)
DBG("Filter redraw by snapshot change!");
else if (_evas_filter_obscured_region_changed(pd))
DBG("Filter redraw by obscure regions change!");
else redraw = EINA_FALSE;
// Scan proxies to find if any changed
if (!redraw && pd->data->sources)
{
Evas_Filter_Proxy_Binding *pb;
Evas_Object_Protected_Data *source;
Eina_Iterator *iter;
iter = eina_hash_iterator_data_new(pd->data->sources);
EINA_ITERATOR_FOREACH(iter, pb)
{
source = efl_data_scope_get(pb->eo_source, EFL_CANVAS_OBJECT_CLASS);
if (source->changed)
{
redraw = EINA_TRUE;
break;
}
}
eina_iterator_free(iter);
}
if (!redraw)
{
// Render this image only
if (use_map)
{
ENFN->image_map_draw(ENDT, context, surface, previous,
obj->map->spans, EINA_TRUE, 0, do_async);
}
else
{
ENFN->image_draw(ENDT, context,
surface, previous,
0, 0, W, H, // src
X + x, Y + y, W, H, // dst
EINA_FALSE, // smooth
do_async);
}
return EINA_TRUE;
}
}
else
{
_evas_filter_state_set_internal(pd->data->chain, pd);
}
filter = pd->data->context;
if (filter)
{
int prev_w, prev_h;
Eina_Bool was_async;
was_async = evas_filter_context_async_get(filter);
evas_filter_context_size_get(filter, &prev_w, &prev_h);
if ((!pd->data->reuse) || (was_async != do_async) ||
(prev_w != W) || (prev_h != H))
{
fcow = FCOW_BEGIN(pd);
fcow->context = NULL;
FCOW_END(fcow, pd);
evas_filter_context_destroy(filter);
filter = NULL;
}
}
if (filter)
{
ok = evas_filter_context_program_use(filter, pd->data->chain, EINA_TRUE, X, Y);
if (!ok)
{
fcow = FCOW_BEGIN(pd);
fcow->context = NULL;
FCOW_END(fcow, pd);
evas_filter_context_destroy(filter);
filter = NULL;
}
}
if (!filter)
{
filter = evas_filter_context_new(obj->layer->evas, do_async, 0);
// Run script
ok = evas_filter_context_program_use(filter, pd->data->chain, EINA_FALSE, X, Y);
if (!filter || !ok)
{
ERR("Parsing failed?");
evas_filter_context_destroy(filter);
if (!pd->data->invalid)
{
fcow = FCOW_BEGIN(pd);
fcow->invalid = EINA_TRUE;
FCOW_END(fcow, pd);
}
return EINA_FALSE;
}
}
// Proxies
evas_filter_context_proxy_render_all(filter, eo_obj, EINA_FALSE);
// Draw Context
drawctx = ENFN->context_new(ENDT);
ENFN->context_color_set(ENDT, drawctx, 255, 255, 255, 255);
// Set obscured region
evas_filter_context_obscured_region_set(filter, pd->data->obscured);
// Allocate all buffers now
evas_filter_context_buffers_allocate_all(filter);
evas_filter_target_set(filter, context, surface, X + x, Y + y,
use_map ? obj->map->spans : NULL);
// Request rendering from the object itself (child class)
evas_filter_program_padding_get(pd->data->chain, &pad, NULL);
ok = evas_filter_input_render(eo_obj, filter, drawctx, NULL, pad.l, pad.r, pad.t, pad.b, 0, 0, do_async);
if (!ok) ERR("Filter input render failed.");
ENFN->context_free(ENDT, drawctx);
// Add post-run callback and run filter
evas_filter_context_post_run_callback_set(filter, _filter_cb, pd);
fcow = FCOW_BEGIN(pd);
fcow->context = filter;
fcow->changed = EINA_FALSE;
fcow->async = do_async;
fcow->prev_obscured = fcow->obscured;
fcow->prev_padding = fcow->padding;
fcow->padding = pad;
fcow->invalid = EINA_FALSE;
if (!invalid) evas_filter_program_padding_get(pgm, NULL, &fcow->padding);
fcow->chain = pgm;
fcow->invalid = invalid;
FCOW_END(fcow, pd);
if (invalid) return EINA_FALSE;
}
else if (previous && !pd->data->changed)
{
Eina_Bool redraw = EINA_TRUE;
ok = evas_filter_context_run(filter);
if (_evas_filter_state_set_internal(pd->data->chain, pd))
DBG("Filter redraw by state change!");
else if (obj->changed)
DBG("Filter redraw by object content change!");
else if (obj->snapshot_needs_redraw)
DBG("Filter redraw by snapshot change!");
else if (_evas_filter_obscured_region_changed(pd))
DBG("Filter redraw by obscure regions change!");
else redraw = EINA_FALSE;
if (ok)
// Scan proxies to find if any changed
if (!redraw && pd->data->sources)
{
DBG("Effect rendering done.");
Evas_Filter_Proxy_Binding *pb;
Evas_Object_Protected_Data *source;
Eina_Iterator *iter;
iter = eina_hash_iterator_data_new(pd->data->sources);
EINA_ITERATOR_FOREACH(iter, pb)
{
source = efl_data_scope_get(pb->eo_source, EFL_CANVAS_OBJECT_CLASS);
if (source->changed)
{
redraw = EINA_TRUE;
break;
}
}
eina_iterator_free(iter);
}
if (!redraw)
{
// Render this image only
if (use_map)
{
ENFN->image_map_draw(ENDT, context, surface, previous,
obj->map->spans, EINA_TRUE, 0, do_async);
}
else
{
ENFN->image_draw(ENDT, context,
surface, previous,
0, 0, W, H, // src
X + x, Y + y, W, H, // dst
EINA_FALSE, // smooth
do_async);
}
return EINA_TRUE;
}
else
}
else
{
_evas_filter_state_set_internal(pd->data->chain, pd);
}
filter = pd->data->context;
if (filter)
{
int prev_w, prev_h;
Eina_Bool was_async;
was_async = evas_filter_context_async_get(filter);
evas_filter_context_size_get(filter, &prev_w, &prev_h);
if ((!pd->data->reuse) || (was_async != do_async) ||
(prev_w != W) || (prev_h != H))
{
fcow = FCOW_BEGIN(pd);
fcow->invalid = EINA_TRUE;
FCOW_END(fcow, pd);
ERR("Rendering failed.");
evas_filter_context_destroy(filter);
FCOW_WRITE(pd, context, NULL);
filter = NULL;
}
}
if (filter)
{
ok = evas_filter_context_program_use(filter, pd->data->chain, EINA_TRUE, X, Y);
if (!ok)
{
evas_filter_context_destroy(filter);
FCOW_WRITE(pd, context, NULL);
filter = NULL;
}
}
if (!filter)
{
filter = evas_filter_context_new(obj->layer->evas, do_async, 0);
// Run script
ok = evas_filter_context_program_use(filter, pd->data->chain, EINA_FALSE, X, Y);
if (!filter || !ok)
{
ERR("Parsing failed?");
evas_filter_context_destroy(filter);
FCOW_WRITE(pd, invalid, EINA_TRUE);
return EINA_FALSE;
}
}
return EINA_FALSE;
// Proxies
evas_filter_context_proxy_render_all(filter, eo_obj, EINA_FALSE);
// Draw Context
drawctx = ENFN->context_new(ENDT);
ENFN->context_color_set(ENDT, drawctx, 255, 255, 255, 255);
// Set obscured region
evas_filter_context_obscured_region_set(filter, pd->data->obscured);
// Allocate all buffers now
evas_filter_context_buffers_allocate_all(filter);
evas_filter_target_set(filter, context, surface, X + x, Y + y,
use_map ? obj->map->spans : NULL);
// Request rendering from the object itself (child class)
evas_filter_program_padding_get(pd->data->chain, &pad, NULL);
ok = evas_filter_input_render(eo_obj, filter, drawctx, NULL, pad.l, pad.r, pad.t, pad.b, 0, 0, do_async);
if (!ok) ERR("Filter input render failed.");
ENFN->context_free(ENDT, drawctx);
// Add post-run callback and run filter
evas_filter_context_post_run_callback_set(filter, _filter_cb, pd);
fcow = FCOW_BEGIN(pd);
fcow->context = filter;
fcow->changed = EINA_FALSE;
fcow->async = do_async;
fcow->prev_obscured = fcow->obscured;
fcow->prev_padding = fcow->padding;
fcow->padding = pad;
fcow->invalid = EINA_FALSE;
FCOW_END(fcow, pd);
// Run the filter now (maybe async)
ok = evas_filter_context_run(filter);
if (!ok)
{
ERR("Filter program failed to run!");
evas_filter_context_destroy(filter);
fcow = FCOW_BEGIN(pd);
fcow->context = NULL;
fcow->invalid = EINA_TRUE;
FCOW_END(fcow, pd);
}
return ok;
}
EOLIAN static void

View File

@ -1798,8 +1798,7 @@ _efl_canvas_image_internal_efl_canvas_filter_internal_filter_input_render(
l, t, r, b, EINA_TRUE, do_async);
ENFN->context_free(output, ctx);
evas_filter_buffer_backing_release(filter, surface);
ENFN->image_free(output, surface);
return EINA_TRUE;
}

View File

@ -212,7 +212,15 @@ _context_destroy(void *data)
void
evas_filter_context_destroy(Evas_Filter_Context *ctx)
{
// FIXME: This is not locked...
if (!ctx) return;
#ifdef FILTERS_DEBUG
EINA_SAFETY_ON_FALSE_RETURN(eina_main_loop_is());
#endif
if (ctx->delete_me) return;
ctx->delete_me = EINA_TRUE;
if (ctx->running)
evas_post_render_job_add(ctx->evas, _context_destroy, ctx);
else
@ -577,18 +585,6 @@ end:
return ret;
}
Eina_Bool
evas_filter_buffer_backing_release(Evas_Filter_Context *ctx,
void *stolen_buffer)
{
if (!stolen_buffer) return EINA_FALSE;
EINA_SAFETY_ON_NULL_RETURN_VAL(ctx, EINA_FALSE);
EINA_SAFETY_ON_FALSE_RETURN_VAL(eina_main_loop_is(), EINA_FALSE);
ENFN->image_free(ENDT, stolen_buffer); // ref--
return EINA_TRUE;
}
static Evas_Filter_Command *
_command_new(Evas_Filter_Context *ctx, Evas_Filter_Mode mode,
Evas_Filter_Buffer *input, Evas_Filter_Buffer *mask,
@ -1820,19 +1816,16 @@ end:
ctx->running = EINA_FALSE;
DEBUG_TIME_END();
if (ctx->post_run.cb)
ctx->post_run.cb(ctx, ctx->post_run.data, ok);
return ok;
}
static void
_filter_thread_run_cb(void *data)
{
Evas_Filter_Context *ctx = data;
Eina_Bool success;
success = _filter_chain_run(ctx);
if (ctx->post_run.cb)
ctx->post_run.cb(ctx, ctx->post_run.data, success);
_filter_chain_run(data);
}
static void
@ -1887,15 +1880,11 @@ _filter_obscured_region_calc(Evas_Filter_Context *ctx)
Eina_Bool
evas_filter_context_run(Evas_Filter_Context *ctx)
{
Eina_Bool ret;
EINA_SAFETY_ON_NULL_RETURN_VAL(ctx, EINA_FALSE);
if (!ctx->commands)
return EINA_TRUE;
_filter_obscured_region_calc(ctx);
ctx->run_count++;
ctx->running = EINA_TRUE;
if (ctx->async)
{
@ -1903,12 +1892,7 @@ evas_filter_context_run(Evas_Filter_Context *ctx)
return EINA_TRUE;
}
ctx->run_count++;
ret = _filter_chain_run(ctx);
if (ctx->post_run.cb)
ctx->post_run.cb(ctx, ctx->post_run.data, ret);
return ret;
return _filter_chain_run(ctx);
}

View File

@ -8,10 +8,6 @@
extern int _evas_filter_log_dom;
#define EVAS_FILTER_LOG_COLOR EINA_COLOR_LIGHTBLUE
//#ifdef DEBUG
# define FILTERS_DEBUG
//#endif
#ifdef ERR
# undef ERR
#endif
@ -159,9 +155,10 @@ struct _Evas_Filter_Context
Eina_Bool color_use : 1;
} target;
volatile int running;
int run_count;
Eina_Bool running : 1;
Eina_Bool delete_me : 1;
Eina_Bool async : 1;
Eina_Bool has_proxies : 1;
Eina_Bool gl : 1;

View File

@ -32,6 +32,10 @@
# endif
#endif /* ! _WIN32 */
//#ifdef DEBUG
# define FILTERS_DEBUG
//#endif
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;
@ -158,7 +162,6 @@ int evas_filter_buffer_empty_new(Evas_Filter_Context *ctx,
int evas_filter_buffer_proxy_new(Evas_Filter_Context *ctx, Evas_Filter_Proxy_Binding *pb, int *w, int *h);
void *evas_filter_buffer_backing_get(Evas_Filter_Context *ctx, int bufid, Eina_Bool render);
Eina_Bool evas_filter_buffer_backing_set(Evas_Filter_Context *ctx, int bufid, void *engine_buffer);
Eina_Bool evas_filter_buffer_backing_release(Evas_Filter_Context *ctx, void *stolen_buffer);
Eina_Bool evas_filter_context_run(Evas_Filter_Context *ctx);