evas filters: Cleanup unused buffers

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.
This commit is contained in:
Jean-Philippe Andre 2017-04-05 19:19:36 +09:00
parent 01a4ecd92c
commit 30ac315631
8 changed files with 70 additions and 42 deletions

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

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

View 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

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

View 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

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

View 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

View 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