evas filters: Cleanup unused buffers
authorJean-Philippe Andre <jp.andre@samsung.com>
Wed, 5 Apr 2017 10:19:36 +0000 (19:19 +0900)
committerJean-Philippe Andre <jp.andre@samsung.com>
Fri, 14 Apr 2017 04:57:04 +0000 (13:57 +0900)
This might not be used as over two consecutive runs all the
same buffers should be used. But it could happen if some
parameters in the filter change (eg. blur radius).

Fixes major (GPU) memory leaks. Reuse mode is still leaking.

src/lib/evas/canvas/evas_filter_mixin.c
src/lib/evas/canvas/evas_object_textblock.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
src/modules/evas/engines/gl_generic/evas_ector_gl_buffer.c
src/modules/evas/engines/gl_generic/evas_ector_gl_image_buffer.c

index 31a7628..742a9e8 100644 (file)
@@ -65,7 +65,12 @@ struct _Evas_Filter_Post_Render_Data
    Eina_Bool success;
 };
 
-static const Evas_Object_Filter_Data evas_filter_data_cow_default = {};
+// FIXME: This should be enabled (with proper heuristics)
+#define FILTER_CONTEXT_REUSE EINA_FALSE
+
+static const Evas_Object_Filter_Data evas_filter_data_cow_default = {
+   .reuse = FILTER_CONTEXT_REUSE
+};
 Eina_Cow *evas_object_filter_cow = NULL;
 
 void
@@ -100,6 +105,7 @@ _filter_end_sync(Evas_Filter_Context *ctx, Evas_Object_Protected_Data *obj,
    Eina_Bool destroy = !pd->data->reuse;
    Evas_Object_Filter_Data *fcow;
    Eo *eo_obj = obj->object;
+   void *output = NULL;
 
    if (!success)
      {
@@ -110,36 +116,25 @@ _filter_end_sync(Evas_Filter_Context *ctx, Evas_Object_Protected_Data *obj,
      }
    else
      {
-        void *output = evas_filter_buffer_backing_get(ctx, EVAS_FILTER_BUFFER_OUTPUT_ID, EINA_FALSE);
-
-        fcow = FCOW_BEGIN(pd);
-        fcow->output = output;
-        FCOW_END(fcow, pd);
+        output = evas_filter_buffer_backing_get(ctx, EVAS_FILTER_BUFFER_OUTPUT_ID, EINA_FALSE);
+        FCOW_WRITE(pd, output, output);
      }
 
    if (EINA_UNLIKELY(ctx != pd->data->context))
      {
         ERR("Filter context has changed! Destroying it now...");
-        fcow = FCOW_BEGIN(pd);
-        evas_filter_context_destroy(fcow->context);
-        fcow->context = NULL;
-        FCOW_END(fcow, pd);
+        evas_filter_context_destroy(pd->data->context);
         destroy = EINA_TRUE;
      }
 
+   evas_filter_buffer_backing_release(ctx, previous);
    if (destroy)
      {
-        evas_filter_buffer_backing_release(ctx, previous);
         evas_filter_context_destroy(ctx);
         ctx = NULL;
      }
 
-   if (pd->data->context != ctx)
-     {
-        fcow = FCOW_BEGIN(pd);
-        fcow->context = ctx;
-        FCOW_END(fcow, pd);
-     }
+   FCOW_WRITE(pd, context, ctx);
 }
 
 static void
@@ -401,7 +396,7 @@ evas_filter_object_render(Eo *eo_obj, Evas_Object_Protected_Data *obj,
 
         if (filter)
           {
-             ok = evas_filter_context_program_reuse(filter, pd->data->chain, X, Y);
+             ok = evas_filter_context_program_use(filter, pd->data->chain, EINA_TRUE, X, Y);
              if (!ok)
                {
                   fcow = FCOW_BEGIN(pd);
index abf076c..2e93b11 100644 (file)
@@ -13002,6 +13002,7 @@ _filter_sync_end(Evas_Filter_Context *ctx, Eina_Bool success)
 
    if (filter->ti)
      {
+        // FIXME: LEAK HERE!
         filter->output = evas_filter_buffer_backing_get(ctx, EVAS_FILTER_BUFFER_OUTPUT_ID, EINA_FALSE);
         if (filter->ti->parent.format->gfx_filter)
           filter->ti->parent.format->gfx_filter->invalid = !success;
index 419c662..988fbe5 100644 (file)
@@ -155,8 +155,12 @@ evas_filter_context_proxy_render_all(Evas_Filter_Context *ctx, Eo *eo_obj,
                void *old_surface;
 
                old_surface = evas_ector_buffer_drawable_image_get(fb->buffer);
-               if (old_surface && (old_surface != proxy_surface))
-                 _filter_buffer_backing_free(fb);
+               if (old_surface)
+                 {
+                    evas_ector_buffer_engine_image_release(fb->buffer, old_surface);
+                    if (old_surface && (old_surface != proxy_surface))
+                      _filter_buffer_backing_free(fb);
+                 }
             }
           XDBG("Source #%d '%s' has dimensions %dx%d", fb->id, fb->source_name, fb->w, fb->h);
           if (!fb->buffer) fb->buffer = ENFN->ector_buffer_wrap(ENDT, obj->layer->evas->evas, source->proxy->surface);
@@ -164,15 +168,12 @@ evas_filter_context_proxy_render_all(Evas_Filter_Context *ctx, Eo *eo_obj,
        }
 }
 
-Eina_Bool
-evas_filter_context_program_reuse(Evas_Filter_Context *ctx, Evas_Filter_Program *pgm, int x, int y)
+void
+_evas_filter_context_program_reuse(Evas_Filter_Context *ctx)
 {
    Evas_Filter_Buffer *fb;
    Eina_List *li;
 
-   EINA_SAFETY_ON_NULL_RETURN_VAL(ctx, EINA_FALSE);
-   EINA_SAFETY_ON_NULL_RETURN_VAL(pgm, EINA_FALSE);
-
    _filter_buffer_unlock_all(ctx);
 
    EINA_LIST_FOREACH(ctx->buffers, li, fb)
@@ -194,9 +195,9 @@ evas_filter_context_program_reuse(Evas_Filter_Context *ctx, Evas_Filter_Program
         ENFN->rectangle_draw(ENDT, dc, surface, 0, 0, fb->w, fb->h, ctx->async);
         ENFN->context_free(ENDT, dc);
         fb->dirty = EINA_FALSE;
-     }
 
-   return evas_filter_context_program_use(ctx, pgm, EINA_TRUE, x, y);
+        evas_ector_buffer_engine_image_release(fb->buffer, surface);
+     }
 }
 
 static void
@@ -268,29 +269,34 @@ evas_filter_context_buffers_allocate_all(Evas_Filter_Context *ctx)
 {
    Evas_Filter_Command *cmd;
    Evas_Filter_Buffer *fb;
-   Eina_List *li;
+   Eina_List *li, *li2;
    unsigned w, h;
 
-   if (ctx->run_count > 0) return EINA_TRUE;
-
    EINA_SAFETY_ON_NULL_RETURN_VAL(ctx, EINA_FALSE);
    w = ctx->w;
    h = ctx->h;
 
    XDBG("Allocating all buffers based on output size %ux%u", w, h);
 
+   EINA_LIST_FOREACH(ctx->buffers, li, fb)
+     fb->cleanup = EINA_TRUE;
+
    EINA_INLIST_FOREACH(ctx->commands, cmd)
      {
         Evas_Filter_Fill_Mode fillmode = cmd->draw.fillmode;
         Evas_Filter_Buffer *in, *out;
 
         in = cmd->input;
+        in->cleanup = EINA_FALSE;
         if (!in->w && !in->h)
           {
              in->w = w;
              in->h = h;
           }
 
+        if (cmd->mask)
+          cmd->mask->cleanup = EINA_FALSE;
+
         // FIXME: No need for stretch buffers with GL!
         if (fillmode & EVAS_FILTER_FILL_MODE_STRETCH_XY)
           {
@@ -326,6 +332,7 @@ evas_filter_context_buffers_allocate_all(Evas_Filter_Context *ctx)
                        fb ? fb->id : -1, sw, sh, in->alpha_only ? "alpha" : "rgba");
                   if (!fb) goto alloc_fail;
                   fb->transient = EINA_TRUE;
+                  fb->cleanup = EINA_FALSE;
                }
           }
 
@@ -342,9 +349,11 @@ evas_filter_context_buffers_allocate_all(Evas_Filter_Context *ctx)
                   fb ? fb->id : -1, sw, sh, in->alpha_only ? "alpha" : "rgba");
              if (!fb) goto alloc_fail;
              fb->transient = EINA_TRUE;
+             fb->cleanup = EINA_FALSE;
           }
 
         out = cmd->output;
+        out->cleanup = EINA_FALSE;
         if (!out->w && !out->h)
           {
              out->w = w;
@@ -356,7 +365,12 @@ evas_filter_context_buffers_allocate_all(Evas_Filter_Context *ctx)
      {
         Eina_Bool render = EINA_FALSE, draw = EINA_FALSE;
 
-        if (fb->buffer || fb->source)
+        if (fb->source)
+          {
+             fb->cleanup = EINA_FALSE;
+             continue;
+          }
+        if (fb->buffer || fb->cleanup)
           continue;
 
         if (!fb->w && !fb->h)
@@ -378,6 +392,16 @@ evas_filter_context_buffers_allocate_all(Evas_Filter_Context *ctx)
         if (!fb->buffer) goto alloc_fail;
      }
 
+   EINA_LIST_FOREACH_SAFE(ctx->buffers, li, li2, fb)
+     {
+        if (fb->cleanup)
+          {
+             XDBG("Cleanup buffer #%d %dx%d %s", fb->id, fb->w, fb->h, fb->alpha_only ? "alpha" : "rgba");
+             ctx->buffers = eina_list_remove_list(ctx->buffers, li);
+             _buffer_free(fb);
+          }
+     }
+
    return EINA_TRUE;
 
 alloc_fail:
@@ -527,25 +551,30 @@ evas_filter_buffer_backing_set(Evas_Filter_Context *ctx, int bufid,
                                void *engine_buffer)
 {
    Evas_Filter_Buffer *fb;
+   Eina_Bool ret = EINA_FALSE;
+   Eo *buffer = NULL;
 
    fb = _filter_buffer_get(ctx, bufid);
    if (!fb) return EINA_FALSE;
 
-   EINA_SAFETY_ON_FALSE_RETURN_VAL(!fb->buffer, EINA_FALSE);
-
    if (!engine_buffer)
      {
-        fb->buffer = _ector_buffer_create(fb, fb->is_render, EINA_FALSE);
+        buffer = _ector_buffer_create(fb, fb->is_render, EINA_FALSE);
         XDBG("Allocated buffer #%d of size %ux%u %s: %p",
              fb->id, fb->w, fb->h, fb->alpha_only ? "alpha" : "rgba", fb->buffer);
-        return fb->buffer ? EINA_TRUE : EINA_FALSE;
+        ret = buffer ? EINA_TRUE : EINA_FALSE;
+        goto end;
      }
 
-   if (fb->buffer) return EINA_FALSE;
-   if (fb->is_render) return EINA_FALSE;
+   if (fb->is_render) goto end;
 
-   fb->buffer = ENFN->ector_buffer_wrap(ENDT, ctx->evas->evas, engine_buffer);
-   return EINA_TRUE;
+   buffer = ENFN->ector_buffer_wrap(ENDT, ctx->evas->evas, engine_buffer);
+   ret = EINA_TRUE;
+
+end:
+   if (fb->buffer != buffer) efl_unref(fb->buffer);
+   fb->buffer = buffer;
+   return ret;
 }
 
 Eina_Bool
index ccf2806..ce1e25a 100644 (file)
@@ -3486,6 +3486,8 @@ evas_filter_context_program_use(Evas_Filter_Context *ctx,
 
    XDBG("Using program '%s' for context %p", pgm->name, ctx);
 
+   if (reuse) _evas_filter_context_program_reuse(ctx);
+
    // Copy current state (size, edje state val, color class, etc...)
    ctx->w = pgm->state.w;
    ctx->h = pgm->state.h;
index 576cef3..df02000 100644 (file)
@@ -265,6 +265,7 @@ struct _Evas_Filter_Buffer
    Eina_Bool locked : 1;      // internal flag
    Eina_Bool dirty : 1;       // Marked as dirty as soon as a command writes to it
    Eina_Bool is_render : 1;   // Is render target of a filter using engine functions (ie. needs FBO in GL)
+   Eina_Bool cleanup : 1;     // Needs cleaning up if not allocated
 };
 
 enum _Evas_Filter_Interpolation_Mode
@@ -295,6 +296,7 @@ Evas_Filter_Buffer *evas_filter_buffer_scaled_get(Evas_Filter_Context *ctx, Evas
 Eina_Bool           evas_filter_interpolate(DATA8* output /* 256 values */, int *points /* 256 values */, Evas_Filter_Interpolation_Mode mode);
 int evas_filter_smallest_pow2_larger_than(int val);
 
+void _evas_filter_context_program_reuse(Evas_Filter_Context *ctx);
 void evas_filter_parser_shutdown(void);
 
 #define E_READ  ECTOR_BUFFER_ACCESS_FLAG_READ
index ca79b36..f273064 100644 (file)
@@ -148,7 +148,6 @@ Eina_Bool                evas_filter_context_async_get(Evas_Filter_Context *ctx)
 void                     evas_filter_context_size_get(Evas_Filter_Context *ctx, int *w, int *H);
 void                     evas_filter_context_destroy(Evas_Filter_Context *ctx);
 Eina_Bool                evas_filter_context_program_use(Evas_Filter_Context *ctx, Evas_Filter_Program *pgm, Eina_Bool reuse, int object_x, int object_y);
-Eina_Bool                evas_filter_context_program_reuse(Evas_Filter_Context *ctx, Evas_Filter_Program *pgm, int x, int y);
 void                     evas_filter_context_proxy_render_all(Evas_Filter_Context *ctx, Eo *eo_obj, Eina_Bool do_async);
 void                     evas_filter_context_post_run_callback_set(Evas_Filter_Context *ctx, Evas_Filter_Cb cb, void *data);
 #define                  evas_filter_context_autodestroy(ctx) evas_filter_context_post_run_callback_set(ctx, ((Evas_Filter_Cb) evas_filter_context_destroy), ctx)
index d948634..108a1c9 100644 (file)
@@ -5,7 +5,6 @@
 #define ECTOR_GL_BUFFER_BASE_PROTECTED
 
 #include "evas_common_private.h"
-#include "evas_gl_private.h"
 
 #include <gl/Ector_GL.h>
 #include "gl/ector_gl_private.h"
@@ -17,6 +16,7 @@
 
 #include "evas_ector_buffer.eo.h"
 #include "evas_ector_gl_buffer.eo.h"
+#include "evas_gl_private.h"
 
 #define MY_CLASS EVAS_ECTOR_GL_BUFFER_CLASS
 
index 3d3bb2e..d6be151 100644 (file)
@@ -7,7 +7,6 @@
 #define ECTOR_GL_BUFFER_BASE_PROTECTED
 
 #include "evas_common_private.h"
-#include "evas_gl_private.h"
 
 #include <gl/Ector_GL.h>
 #include "gl/ector_gl_private.h"
@@ -20,6 +19,7 @@
 #include "evas_ector_buffer.eo.h"
 #include "evas_ector_gl_buffer.eo.h"
 #include "evas_ector_gl_image_buffer.eo.h"
+#include "evas_gl_private.h"
 
 #define MY_CLASS EVAS_ECTOR_GL_IMAGE_BUFFER_CLASS