Evas filters: Fix async render and unify SW + GL
authorJean-Philippe Andre <jp.andre@samsung.com>
Thu, 7 Jan 2016 07:54:42 +0000 (16:54 +0900)
committerJean-Philippe Andre <jp.andre@samsung.com>
Thu, 7 Jan 2016 08:47:48 +0000 (17:47 +0900)
SW async render mode was broken because it was party sync, partly
async (bad hack in a recent commit). This patch fixes that by
using a proper callback for render_post (main loop).

Since the engines and ector now abstract all pixel access functions,
the only difference between GL and SW is the async rendering.

src/lib/evas/canvas/evas_filter_mixin.c
src/lib/evas/filters/evas_filter.c
src/lib/evas/filters/evas_filter_private.h

index 6cf205a..ebafda5 100644 (file)
 #define FCOW_END(_fcow, _pd) eina_cow_done(evas_object_filter_cow, (const Eina_Cow_Data**)&(_pd->data), _fcow, EINA_TRUE)
 
 typedef struct _Evas_Filter_Data Evas_Filter_Data;
+typedef struct _Evas_Filter_Post_Render_Data Evas_Filter_Post_Render_Data;
+
 struct _Evas_Filter_Data
 {
    const Evas_Object_Filter_Data *data;
+   Eina_Bool has_cb;
+   SLK(lck);
+   Eina_List *post_data;
+};
+
+struct _Evas_Filter_Post_Render_Data
+{
+   Evas_Filter_Context *ctx;
+   Eina_Bool success;
 };
 
 static void
-_filter_cb(Evas_Filter_Context *ctx, void *data, Eina_Bool success)
+_filter_end_sync(Evas_Filter_Context *ctx, Evas_Object_Protected_Data *obj,
+                 Evas_Filter_Data *pd, Eina_Bool success)
 {
-   Eo *eo_obj = data;
-   Evas_Filter_Data *pd = eo_data_scope_get(eo_obj, MY_CLASS);
+   void *previous = pd->data->output;
+   Eo *eo_obj = obj->object;
 
-   // FIXME: This needs to run in the main loop
+#ifdef DEBUG
+   EINA_SAFETY_ON_FALSE_RETURN(eina_main_loop_is());
+#endif
 
-   // Redraw text with normal styles in case of failure
    if (!success)
      {
-        Evas_Object_Protected_Data *obj = eo_data_scope_get(eo_obj, EVAS_OBJECT_CLASS);
-
         ERR("Filter failed at runtime!");
         eo_do(eo_obj,
               evas_filter_invalid_set(EINA_TRUE);
@@ -53,9 +64,57 @@ _filter_cb(Evas_Filter_Context *ctx, void *data, Eina_Bool success)
      }
 
    // Destroy context as we won't reuse it.
+   evas_filter_buffer_backing_release(ctx, previous);
    evas_filter_context_destroy(ctx);
 }
 
+static Eina_Bool
+_render_post_cb(void *data, Eo *evas EINA_UNUSED,
+                const Eo_Event_Description2 *desc EINA_UNUSED,
+                void *event_info EINA_UNUSED)
+{
+   Eo *eo_obj = data;
+   Evas_Object_Protected_Data *obj = eo_data_scope_get(eo_obj, EVAS_OBJECT_CLASS);
+   Evas_Filter_Data *pd = eo_data_scope_get(eo_obj, MY_CLASS);
+   Eina_List *post_data;
+   Evas_Filter_Post_Render_Data *task;
+
+   SLKL(pd->lck);
+   post_data = pd->post_data;
+   pd->post_data = NULL;
+   SLKU(pd->lck);
+
+   EINA_LIST_FREE(post_data, task)
+     {
+        _filter_end_sync(task->ctx, obj, pd, task->success);
+        free(task);
+     }
+
+   return EO_CALLBACK_CONTINUE;
+}
+
+static void
+_filter_cb(Evas_Filter_Context *ctx, void *data, Eina_Bool success)
+{
+   Eo *eo_obj = data;
+   Evas_Object_Protected_Data *obj = eo_data_scope_get(eo_obj, EVAS_OBJECT_CLASS);
+   Evas_Filter_Data *pd = eo_data_scope_get(eo_obj, MY_CLASS);
+   Evas_Filter_Post_Render_Data *post_data;
+
+   if (!pd->data->async)
+     {
+        _filter_end_sync(ctx, obj, pd, success);
+        return;
+     }
+
+   post_data = calloc(1, sizeof(*post_data));
+   post_data->success = success;
+   post_data->ctx = ctx;
+   SLKL(pd->lck);
+   pd->post_data = eina_list_append(pd->post_data, post_data);
+   SLKU(pd->lck);
+}
+
 static void
 _filter_source_hash_free_cb(void *data)
 {
@@ -98,10 +157,6 @@ evas_filter_object_render(Eo *eo_obj, Evas_Object_Protected_Data *obj,
 {
    Evas_Filter_Data *pd = eo_data_scope_get(eo_obj, MY_CLASS);
 
-   // FIXME: Forcing sync render for all engines! (cb needs the main loop)
-   if (do_async) WRN("Disarding async render flag!");
-   do_async = EINA_FALSE;
-
    if (!pd->data->invalid && (pd->data->chain || pd->data->code))
      {
         int X, Y, W, H, l = 0, r = 0, t = 0, b = 0;
@@ -206,6 +261,15 @@ evas_filter_object_render(Eo *eo_obj, Evas_Object_Protected_Data *obj,
 
              if (!redraw)
                {
+                  if (pd->has_cb)
+                    {
+                       // Post render callback is not required anymore
+                       Evas *e = obj->layer->evas->evas;
+                       eo_do(e, eo_event_callback_del(EVAS_CANVAS_EVENT_RENDER_POST,
+                                                      _render_post_cb, eo_obj));
+                       pd->has_cb = EINA_FALSE;
+                    }
+
                   // Render this image only
                   ENFN->image_draw(ENDT, context,
                                    surface, previous,
@@ -251,9 +315,6 @@ evas_filter_object_render(Eo *eo_obj, Evas_Object_Protected_Data *obj,
         evas_filter_context_buffers_allocate_all(filter);
         evas_filter_target_set(filter, context, surface, X + x, Y + y);
 
-        // Release previous output
-        evas_filter_buffer_backing_release(filter, previous);
-
         // Request rendering from the object itself (child class)
         evas_filter_program_padding_get(pd->data->chain, &l, &r, &t, &b);
         eo_do(eo_obj, ok = evas_filter_input_render(filter, drawctx, l, r, t, b, do_async));
@@ -262,11 +323,17 @@ evas_filter_object_render(Eo *eo_obj, Evas_Object_Protected_Data *obj,
         ENFN->context_free(ENDT, drawctx);
 
         // Add post-run callback and run filter
+        if (do_async && !pd->has_cb)
+          {
+             Evas *e = obj->layer->evas->evas;
+             eo_do(e, eo_event_callback_add(EVAS_CANVAS_EVENT_RENDER_POST,
+                                            _render_post_cb, eo_obj));
+             pd->has_cb = EINA_TRUE;
+          }
         evas_filter_context_post_run_callback_set(filter, _filter_cb, eo_obj);
         ok = evas_filter_run(filter);
 
         fcow = FCOW_BEGIN(pd);
-        fcow->output = NULL;
         fcow->changed = EINA_FALSE;
         fcow->async = do_async;
         if (!ok) fcow->invalid = EINA_TRUE;
@@ -520,6 +587,7 @@ EOLIAN void
 _evas_filter_constructor(Eo *eo_obj EINA_UNUSED, Evas_Filter_Data *pd)
 {
    pd->data = eina_cow_alloc(evas_object_filter_cow);
+   SLKI(pd->lck);
 }
 
 EOLIAN void
@@ -549,6 +617,13 @@ _evas_filter_destructor(Eo *eo_obj, Evas_Filter_Data *pd)
    evas_filter_program_del(pd->data->chain);
    eina_stringshare_del(pd->data->code);
    eina_cow_free(evas_object_filter_cow, (const Eina_Cow_Data **) &pd->data);
+   if (pd->has_cb)
+     {
+        Evas *e = obj->layer->evas->evas;
+        eo_do(e, eo_event_callback_del(EVAS_CANVAS_EVENT_RENDER_POST,
+                                       _render_post_cb, eo_obj));
+     }
+   SLKD(pd->lck);
 }
 
 EOLIAN void
index 73dc047..36aab1c 100644 (file)
@@ -61,17 +61,6 @@ evas_filter_context_new(Evas_Public_Data *evas, Eina_Bool async)
    ctx->evas = evas;
    ctx->async = async;
 
-   /* Note:
-    * For now, we need to detect whether the engine is full SW or GL.
-    * In case of GL, we will have two buffers in parallel:
-    * RGBA_Image backing and a GL texture (glimage). We can't just leave it
-    * to the engine to handle the image data since it will try to discard the
-    * original buffer as early as possible to save memory, but we still need
-    * it for filtering.
-    * In the future, Evas_Engine functions need to be added to abstract this
-    * better and implement filters direcly with shaders.
-    */
-   ctx->gl_engine = (evas->engine.func->gl_surface_read_pixels != NULL);
    return ctx;
 }
 
@@ -462,14 +451,11 @@ evas_filter_buffer_backing_release(Evas_Filter_Context *ctx,
    if (!stolen_buffer) return EINA_FALSE;
    EINA_SAFETY_ON_NULL_RETURN_VAL(ctx, EINA_FALSE);
 
-   if (ctx->async)
-     evas_unref_queue_image_put(ctx->evas, stolen_buffer);
-   else
-     {
-        ctx->post_run.buffers_to_free =
-              eina_list_append(ctx->post_run.buffers_to_free, stolen_buffer);
-     }
+#ifdef DEBUG
+   EINA_SAFETY_ON_FALSE_RETURN_VAL(eina_main_loop_is(), EINA_FALSE);
+#endif
 
+   ENFN->image_free(ENDT, stolen_buffer);
    return EINA_TRUE;
 }
 
@@ -1286,28 +1272,6 @@ evas_filter_target_set(Evas_Filter_Context *ctx, void *draw_context,
      ctx->evas->engine.func->image_free(ctx->evas->engine.data.output, ctx->target.mask);
    ctx->target.mask = mask;
 
-#if 0
-   if (ctx->gl_engine)
-     {
-        // Since GL has sync rendering, draw_context is safe to keep around
-        Evas_Filter_Buffer *fb;
-
-        ctx->target.context = draw_context;
-
-        fb = _filter_buffer_get(ctx, EVAS_FILTER_BUFFER_OUTPUT_ID);
-        EINA_SAFETY_ON_NULL_RETURN_VAL(fb, EINA_FALSE);
-        if (!fb->backing)
-          return EINA_FALSE;
-
-        fb->glimage = ENFN->image_new_from_data
-          (ENDT, fb->w, fb->h, fb->backing->image.data, EINA_TRUE,
-           fb->backing->cache_entry.space);
-
-        XDBG("Set target as #%d (%p) and output #%d (%p, gl %p)",
-            ctx->target.bufid, surface, fb->id, fb->backing, fb->glimage);
-     }
-#endif
-
    return EINA_TRUE;
 }
 
@@ -1326,35 +1290,9 @@ _filter_target_render(Evas_Filter_Context *ctx)
    EINA_SAFETY_ON_NULL_RETURN_VAL(src, EINA_FALSE);
    EINA_SAFETY_ON_NULL_RETURN_VAL(dst, EINA_FALSE);
 
-#if 0
-   if (ctx->gl_engine)
-     {
-        drawctx = ctx->target.context;
-        surface = dst->glimage;
-        if (src->glimage)
-          {
-             XDBG("Using glimage from output buffer.");
-             if (src->backing)
-               ENFN->image_data_put(ENDT, src->glimage, src->backing->image.data);
-          }
-        else
-          {
-             RGBA_Image *im = src->backing;
-
-             XDBG("Creating glimage from output buffer.");
-             src->glimage = ENFN->image_new_from_data(ENDT, src->w, src->h,
-                                               im->image.data, EINA_TRUE,
-                                               EVAS_COLORSPACE_ARGB8888);
-          }
-        image = src->glimage;
-     }
-   else
-#endif
-     {
-        drawctx = ENFN->context_new(ENDT);
-        image = _evas_image_get(src->buffer);
-        surface = _evas_image_get(dst->buffer);
-     }
+   drawctx = ENFN->context_new(ENDT);
+   image = _evas_image_get(src->buffer);
+   surface = _evas_image_get(dst->buffer);
    EINA_SAFETY_ON_NULL_RETURN_VAL(image, EINA_FALSE);
    EINA_SAFETY_ON_NULL_RETURN_VAL(surface, EINA_FALSE);
 
@@ -1390,16 +1328,7 @@ _filter_target_render(Evas_Filter_Context *ctx)
                     ctx->target.x, ctx->target.y, src->w, src->h,
                     EINA_TRUE, EINA_FALSE);
 
-   if (ctx->target.mask)
-     ENFN->context_clip_image_unset(ENDT, drawctx);
-
-   if (!ctx->gl_engine)
-     ENFN->context_free(ENDT, drawctx);
-   else if (use_clip)
-     ENFN->context_clip_set(ENDT, drawctx, cx, cy, cw, ch);
-   else
-     ENFN->context_clip_unset(ENDT, drawctx);
-
+   ENFN->context_free(ENDT, drawctx);
    return EINA_TRUE;
 }
 
@@ -1639,9 +1568,6 @@ end:
    ctx->running = EINA_FALSE;
    DEBUG_TIME_END();
 
-   EINA_LIST_FREE(ctx->post_run.buffers_to_free, buffer)
-     ENFN->image_free(ENDT, buffer);
-
    return ok;
 }
 
@@ -1668,9 +1594,9 @@ evas_filter_run(Evas_Filter_Context *ctx)
    if (!ctx->commands)
      return EINA_TRUE;
 
-   if (ctx->gl_engine && !warned)
+   if (ENFN->gl_surface_read_pixels && !warned)
      {
-        DBG("OpenGL support through SW functions, expect low performance!");
+        WRN("OpenGL support through SW functions, expect low performance!");
         warned = 1;
      }
 
index bc13de9..5ad458b 100644 (file)
@@ -126,7 +126,6 @@ struct _Evas_Filter_Context
       /** Post-processing callback. The context can be safely destroyed here. */
       Evas_Filter_Cb cb;
       void *data;
-      Eina_List *buffers_to_free; // Some buffers should be queued for deletion
    } post_run;
 
    struct
@@ -143,7 +142,6 @@ struct _Evas_Filter_Context
    } target;
 
    Eina_Bool async : 1;
-   Eina_Bool gl_engine : 1;
    Eina_Bool running : 1;
    Eina_Bool has_proxies : 1;
 };