evas: Fix excessive redraws of snapshot objects

This commit is contained in:
Jean-Philippe Andre 2017-03-22 20:51:53 +09:00
parent 1bf24f8762
commit b6c5af5edc
3 changed files with 94 additions and 64 deletions

View File

@ -263,12 +263,15 @@ evas_filter_object_render(Eo *eo_obj, Evas_Object_Protected_Data *obj,
}
else if (previous && !pd->data->changed)
{
Eina_Bool redraw = EINA_TRUE;
Eina_Bool redraw;
if (_evas_filter_state_set_internal(pd->data->chain, pd))
redraw = _evas_filter_state_set_internal(pd->data->chain, pd);
if (redraw)
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;

View File

@ -100,12 +100,19 @@ rend_dbg(const char *txt)
#define ENDT evas->engine.data.output
typedef struct _Render_Updates Render_Updates;
typedef struct _Cutout_Margin Cutout_Margin;
struct _Render_Updates
{
void *surface;
Eina_Rectangle *area;
};
struct _Cutout_Margin
{
int l, r, t, b;
};
static Eina_Bool
evas_render_updates_internal(Evas *eo_e, unsigned char make_updates, unsigned char do_draw, Evas_Render_Done_Cb done_func, void *done_data, Eina_Bool do_async);
@ -2615,7 +2622,7 @@ end:
static void
_evas_render_cutout_add(Evas_Public_Data *evas, void *context,
Evas_Object_Protected_Data *obj, int off_x, int off_y,
int cutout_margin)
Cutout_Margin *cutout_margin)
{
Evas_Coord cox = 0, coy = 0, cow = 0, coh = 0;
@ -2630,8 +2637,7 @@ _evas_render_cutout_add(Evas_Public_Data *evas, void *context,
{
Evas_Object_Protected_Data *oo;
oo = obj;
while (oo->cur->clipper)
for (oo = obj; oo->cur->clipper; oo = oo->cur->clipper)
{
if ((oo->cur->clipper->map->cur.map_parent
!= oo->map->cur.map_parent) &&
@ -2642,7 +2648,7 @@ _evas_render_cutout_add(Evas_Public_Data *evas, void *context,
oo->cur->geometry.y,
oo->cur->geometry.w,
oo->cur->geometry.h);
oo = oo->cur->clipper;
if ((cow <= 0) || (coh <= 0)) return;
}
}
}
@ -2655,12 +2661,15 @@ _evas_render_cutout_add(Evas_Public_Data *evas, void *context,
obj->cur->cache.clip.w, obj->cur->cache.clip.h);
}
else return;
cox += cutout_margin + off_x;
coy += cutout_margin + off_y;
cow -= 2 * cutout_margin;
coh -= 2 * cutout_margin;
if (cutout_margin)
{
cox += cutout_margin->l;
coy += cutout_margin->t;
cow -= cutout_margin->l + cutout_margin->r;
coh -= cutout_margin->t + cutout_margin->b;
}
if ((cow <= 0) || (coh <= 0)) return;
ENFN->context_cutout_add(ENDT, context, cox, coy, cow, coh);
ENFN->context_cutout_add(ENDT, context, cox + off_x, coy + off_y, cow, coh);
}
void
@ -2750,11 +2759,10 @@ _rectangle_inside(Eina_Rectangle *big, Eina_Rectangle *small)
return EINA_FALSE;
}
static Eina_Bool
_snapshot_needs_redraw(Evas_Public_Data *evas, Evas_Object_Protected_Data *snap,
Eina_Rectangle *opaque)
static void
_snapshot_redraw_update(Evas_Public_Data *evas, Evas_Object_Protected_Data *snap)
{
Eina_Bool above = EINA_FALSE, ret = EINA_FALSE;
Eina_Bool above = EINA_FALSE, add_rect = EINA_FALSE, need_redraw = EINA_FALSE;
const int x = snap->cur->geometry.x;
const int y = snap->cur->geometry.y;
const int w = snap->cur->geometry.w;
@ -2762,27 +2770,35 @@ _snapshot_needs_redraw(Evas_Public_Data *evas, Evas_Object_Protected_Data *snap,
Evas_Object_Protected_Data *obj;
Evas_Active_Entry *ent;
Eina_Rectangle snap_rect = { x, y, w, h };
Eina_Rectangle opaque = {};
void *surface;
surface = _evas_object_image_surface_get(snap, EINA_FALSE);
if (!surface) ret = EINA_TRUE;
// FIXME: Use evas' standard rectangle logic instead of this bad algo
// TODO: Improve opaque region support, maybe have more than one
// TODO: Also list redraw regions (partial updates)
if (!evas_object_is_visible(snap->object, snap)) return;
surface = _evas_object_image_surface_get(snap, EINA_FALSE);
if (!surface) need_redraw = EINA_TRUE;
if (snap->changed) add_rect = EINA_TRUE;
memset(opaque, 0, sizeof(*opaque));
EINA_INARRAY_FOREACH(&evas->active_objects, ent)
{
obj = ent->obj;
if (obj == snap)
{
if (!need_redraw) break;
above = EINA_TRUE;
continue;
}
if (!above)
{
if (ret) continue;
if (add_rect) continue;
if (!obj->is_smart && obj->changed &&
evas_object_is_in_output_rect(obj->object, obj, x, y, w, h))
ret = EINA_TRUE;
need_redraw = EINA_TRUE;
}
else
{
@ -2794,22 +2810,37 @@ _snapshot_needs_redraw(Evas_Public_Data *evas, Evas_Object_Protected_Data *snap,
obj->cur->geometry.w, obj->cur->geometry.h
};
if ((opaque.w * opaque.h) < (cur.w * cur.h))
{
opaque = cur;
continue;
}
if (!eina_rectangles_intersect(&snap_rect, &cur))
continue;
if (!opaque->w || !opaque->h)
*opaque = cur;
else if (_rectangle_inside(&cur, opaque))
*opaque = cur;
else if (!_rectangle_inside(opaque, &cur))
{
*opaque = (Eina_Rectangle) { 0, 0, 0, 0 };
break;
}
if (!opaque.w || !opaque.h)
opaque = cur;
else if (_rectangle_inside(&cur, &opaque))
opaque = cur;
//else if (!_rectangle_inside(&opaque, &cur))
}
}
}
return ret;
_evas_filter_obscured_region_set(snap, opaque);
snap->snapshot_needs_redraw |= need_redraw;
if (add_rect || need_redraw)
{
// FIXME: Only add necessary rects
// Note that with filters this is extremely tricky: a simple color
// change would mean redraw all. Also blurs, displace, etc... need
// to expand by the cutout_margin (filter padding).
ENFN->output_redraws_rect_add(ENDT,
snap->cur->geometry.x, snap->cur->geometry.y,
snap->cur->geometry.w, snap->cur->geometry.h);
}
}
static Eina_Bool
@ -2818,9 +2849,9 @@ evas_render_updates_internal_loop(Evas *eo_e, Evas_Public_Data *evas,
Evas_Object_Protected_Data *top,
int ux, int uy, int uw, int uh,
int cx, int cy, int cw, int ch,
int fx, int fy, int cutout_margin,
Eina_Bool alpha,
Eina_Bool do_async,
int fx, int fy,
Cutout_Margin *cutout_margin,
Eina_Bool alpha, Eina_Bool do_async,
unsigned int *offset, int level)
{
Evas_Object *eo_obj;
@ -3165,11 +3196,7 @@ evas_render_updates_internal(Evas *eo_e,
for (i = 0; i < e->snapshot_objects.count; i++)
{
obj = eina_array_data_get(&e->snapshot_objects, i);
if (evas_object_is_visible(obj->object, obj))
ENFN->output_redraws_rect_add(ENDT,
obj->cur->geometry.x, obj->cur->geometry.y,
obj->cur->geometry.w, obj->cur->geometry.h);
_snapshot_redraw_update(evas, obj);
}
}
eina_evlog("-render_phase4", eo_e, 0.0, NULL);
@ -3246,21 +3273,22 @@ evas_render_updates_internal(Evas *eo_e,
eina_evlog("+render_snapshots", eo_e, 0.0, NULL);
for (j = e->snapshot_objects.count - 1; j >= 0; j--)
{
Eina_Rectangle output, cr, ur, opaque;
Evas_Object_Protected_Data *snap;
Eina_Rectangle output, cr, ur;
obj = eina_array_data_get(&e->snapshot_objects, j);
snap = eina_array_data_get(&e->snapshot_objects, j);
EINA_RECTANGLE_SET(&output,
obj->cur->geometry.x,
obj->cur->geometry.y,
obj->cur->geometry.w,
obj->cur->geometry.h);
snap->cur->geometry.x,
snap->cur->geometry.y,
snap->cur->geometry.w,
snap->cur->geometry.h);
EINA_RECTANGLE_SET(&ur, ux, uy, uw, uh);
if (eina_rectangle_intersection(&ur, &output) &&
_snapshot_needs_redraw(evas, obj, &opaque))
if (snap->snapshot_needs_redraw &&
eina_rectangle_intersection(&ur, &output))
{
int cutout_margin, fpl = 0, fpr = 0, fpt = 0, fpb = 0;
Cutout_Margin cm = {};
unsigned int restore_offset = offset;
void *pseudo_canvas;
@ -3268,31 +3296,22 @@ evas_render_updates_internal(Evas *eo_e,
ur.x - output.x, ur.y - output.y,
ur.w, ur.h);
pseudo_canvas = _evas_object_image_surface_get(obj, EINA_TRUE);
pseudo_canvas = _evas_object_image_surface_get(snap, EINA_TRUE);
// TODO: List of canvas regions that need redraw
// Get required margin for filters (eg. blur radius)
_evas_filter_radius_get(obj, &fpl, &fpr, &fpt, &fpb);
cutout_margin = MAX(MAX(MAX(fpl, fpr), fpt), fpb);
_evas_filter_radius_get(snap, &cm.l, &cm.r, &cm.t, &cm.b);
// Add obscure region (make it a list?)
_evas_filter_obscured_region_set(obj, opaque);
RD(0, " SNAPSHOT %s [sfc:%p ur:%d,%d %dx%d]\n", RDNAME(obj), pseudo_canvas, ur.x, ur.y, ur.w, ur.h);
RD(0, " SNAPSHOT %s [sfc:%p ur:%d,%d %dx%d]\n", RDNAME(snap), pseudo_canvas, ur.x, ur.y, ur.w, ur.h);
ctx = ENFN->context_new(ENDT);
clean_them |= evas_render_updates_internal_loop(eo_e, e, pseudo_canvas, ctx,
obj,
snap,
ur.x, ur.y, ur.w, ur.h,
cr.x, cr.y, cr.w, cr.h,
fx, fy, cutout_margin, alpha,
do_async,
fx, fy, &cm,
alpha, do_async,
&offset, 1);
ENFN->context_free(ENDT, ctx);
// Force the object has changed for filter to take it into
// account. It won't be in the pending object array.
obj->changed = EINA_TRUE;
offset = restore_offset;
// FIXME: For some reason the arrays are not cleaned and
@ -3321,8 +3340,8 @@ evas_render_updates_internal(Evas *eo_e,
ctx, NULL,
ux, uy, uw, uh,
cx, cy, cw, ch,
fx, fy, 0, alpha,
do_async,
fx, fy, NULL,
alpha, do_async,
&offset, 0);
ENFN->context_free(ENDT, ctx);
@ -3399,6 +3418,13 @@ evas_render_updates_internal(Evas *eo_e,
else if (obj->delete_me != 0) obj->delete_me++;
*/
}
for (i = 0; i < e->snapshot_objects.count; i++)
{
Evas_Object_Protected_Data *snap;
snap = eina_array_data_get(&e->snapshot_objects, i);
snap->snapshot_needs_redraw = EINA_FALSE;
}
eina_evlog("-render_post", eo_e, 0.0, NULL);
IFRD(e->active_objects.len, 0, " ---]\n");

View File

@ -1220,6 +1220,7 @@ struct _Evas_Object_Protected_Data
Eina_Bool no_render : 1; // since 1.15
Eina_Bool legacy : 1; // used legacy constructor
Eina_Bool clean_layer : 1; // destructor option
Eina_Bool snapshot_needs_redraw : 1;
struct {
Eina_Bool pass_events : 1;