evas filters: Implement obscure support for gl blur

This can help with performance when a large region of the
filtered image (eg. snapshot) is fully hidden by an opaque
object. For instance the window border is hidden by the
opaque window content.
This commit is contained in:
Jean-Philippe Andre 2017-03-31 13:10:41 +09:00
parent 08a8072571
commit 45548e8358
2 changed files with 164 additions and 66 deletions

View File

@ -3139,13 +3139,17 @@ _filter_data_flush(Evas_Engine_GL_Context *gc, Evas_GL_Program *prog)
}
static inline void
_filter_data_prepare(Evas_Engine_GL_Context *gc, int pn,
Evas_GL_Program *prog, int count)
_filter_data_alloc(Evas_Engine_GL_Context *gc, int pn, int count)
{
gc->pipe[pn].array.filter_data_count = count;
if (count) gc->pipe[pn].array.filter_data = malloc(count * 2 * sizeof(GLfloat));
else gc->pipe[pn].array.filter_data = NULL;
}
static inline void
_filter_data_prepare(Evas_Engine_GL_Context *gc EINA_UNUSED,
Evas_GL_Program *prog, int count)
{
if (!prog->filter) prog->filter = calloc(1, sizeof(*prog->filter));
if (!prog->filter->attribute.known_locations)
{
@ -3224,7 +3228,8 @@ evas_gl_common_filter_displace_push(Evas_Engine_GL_Context *gc,
// displace properties
gc->pipe[pn].shader.filter.map_tex = map_tex->pt->texture;
gc->pipe[pn].shader.filter.map_nearest = nearest;
_filter_data_prepare(gc, pn, prog, 3);
_filter_data_prepare(gc, prog, 3);
_filter_data_alloc(gc, pn, 3);
sx = x;
sy = y;
@ -3439,7 +3444,7 @@ evas_gl_common_filter_blur_push(Evas_Engine_GL_Context *gc,
const double * const offsets, int count,
double radius, Eina_Bool horiz)
{
double ox1, oy1, ox2, oy2, ox3, oy3, ox4, oy4, pw, ph;
double ox1, oy1, ox2, oy2, ox3, oy3, ox4, oy4, pw, ph, texlen;
GLfloat tx1, ty1, tx2, ty2, tx3, ty3, tx4, ty4;
GLfloat offsetx, offsety;
int r, g, b, a, nomul = 0, pn;
@ -3447,11 +3452,10 @@ evas_gl_common_filter_blur_push(Evas_Engine_GL_Context *gc,
Eina_Bool blend = EINA_TRUE;
Eina_Bool smooth = EINA_TRUE;
Shader_Type type = horiz ? SHD_FILTER_BLUR_X : SHD_FILTER_BLUR_Y;
Eina_Bool update_uniforms = EINA_FALSE;
GLuint *map_tex_data;
double sum;
//shader_array_flush(gc);
r = R_VAL(&gc->dc->mul.col);
g = G_VAL(&gc->dc->mul.col);
b = B_VAL(&gc->dc->mul.col);
@ -3463,9 +3467,59 @@ evas_gl_common_filter_blur_push(Evas_Engine_GL_Context *gc,
sw, sh, dw, dh, smooth, tex, EINA_FALSE,
NULL, EINA_FALSE, EINA_FALSE, 0, 0,
NULL, &nomul, NULL);
_filter_data_flush(gc, prog);
EINA_SAFETY_ON_NULL_RETURN(prog);
pw = tex->pt->w;
ph = tex->pt->h;
texlen = horiz ? pw : ph;
/* Convert double data to RGBA pixel data.
*
* We are not using GL_FLOAT or GL_DOUBLE because:
* - It's not as portable (needs extensions),
* - GL_DOUBLE didn't work during my tests (dunno why),
* - GL_FLOAT didn't seem to carry the proper precision all the way to
* the fragment shader,
* - Real data buffers are not available in GLES 2.0,
* - GL_RGBA is 100% portable.
*/
map_tex_data = alloca(2 * count * sizeof(*map_tex_data));
for (int k = 0; k < count; k++)
{
GLuint val;
if (k == 0) sum = weights[k];
else sum += 2.0 * weights[k];
// Weight is always > 0.0 and < 255.0 by maths
val = (GLuint) (weights[k] * 256.0 * 256.0 * 256.0);
map_tex_data[k] = val;
// Offset is always in [0.0 , 1.0] by definition
val = (GLuint) (offsets[k] * 256.0 * 256.0 * 256.0);
map_tex_data[k + count] = val;
}
// Prepare attributes & uniforms
_filter_data_prepare(gc, prog, 0);
if (!prog->filter->uniform.known_locations)
{
prog->filter->uniform.known_locations = EINA_TRUE;
prog->filter->uniform.blur_count_loc = glGetUniformLocation(prog->prog, "blur_count");
prog->filter->uniform.blur_texlen_loc = glGetUniformLocation(prog->prog, "blur_texlen");
prog->filter->uniform.blur_div_loc = glGetUniformLocation(prog->prog, "blur_div");
}
if ((prog->filter->uniform.blur_count_value != count - 1) ||
(!EINA_FLT_EQ(prog->filter->uniform.blur_texlen_value, texlen)) ||
(!EINA_FLT_EQ(prog->filter->uniform.blur_div_value, sum)))
{
update_uniforms = EINA_TRUE;
shader_array_flush(gc);
}
pn = _evas_gl_common_context_push(type, gc, tex, NULL, prog,
sx, sy, dw, dh, blend, smooth,
0, 0, 0, 0, 0, EINA_FALSE);
@ -3497,42 +3551,7 @@ evas_gl_common_filter_blur_push(Evas_Engine_GL_Context *gc,
pipe_region_expand(gc, pn, dx, dy, dw, dh);
PIPE_GROW(gc, pn, 6);
/* Convert double data to RGBA pixel data.
*
* We are not using GL_FLOAT or GL_DOUBLE because:
* - It's not as portable (needs extensions),
* - GL_DOUBLE didn't work during my tests (dunno why),
* - GL_FLOAT didn't seem to carry the proper precision all the way to
* the fragment shader,
* - Real data buffers are not available in GLES 2.0,
* - GL_RGBA is 100% portable.
*/
map_tex_data = alloca(2 * count * sizeof(*map_tex_data));
for (int k = 0; k < count; k++)
{
GLuint val;
if (k == 0) sum = weights[k];
else sum += 2.0 * weights[k];
// Weight is always > 0.0 and < 255.0 by maths
val = (GLuint) (weights[k] * 256.0 * 256.0 * 256.0);
map_tex_data[k] = val;
// Offset is always in [0.0 , 1.0] by definition
val = (GLuint) (offsets[k] * 256.0 * 256.0 * 256.0);
map_tex_data[k + count] = val;
}
// Prepare attributes & uniforms
_filter_data_prepare(gc, pn, prog, 0);
if (!prog->filter->uniform.known_locations)
{
prog->filter->uniform.known_locations = EINA_TRUE;
prog->filter->uniform.blur_count_loc = glGetUniformLocation(prog->prog, "blur_count");
prog->filter->uniform.blur_texlen_loc = glGetUniformLocation(prog->prog, "blur_texlen");
prog->filter->uniform.blur_div_loc = glGetUniformLocation(prog->prog, "blur_div");
}
_filter_data_alloc(gc, pn, 0);
// Synchronous upload of Nx2 RGBA texture
if (!EINA_DBL_EQ(prog->filter->blur_radius, radius))
@ -3553,10 +3572,10 @@ evas_gl_common_filter_blur_push(Evas_Engine_GL_Context *gc,
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, count, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, map_tex_data);
}
//if (update_uniforms)
if (update_uniforms)
{
prog->filter->uniform.blur_count_value = count - 1;
prog->filter->uniform.blur_texlen_value = horiz ? sw : sh;
prog->filter->uniform.blur_texlen_value = texlen;
prog->filter->uniform.blur_div_value = sum;
glUseProgram(prog->prog);
glUniform1i(prog->filter->uniform.blur_count_loc, prog->filter->uniform.blur_count_value);
@ -3569,9 +3588,6 @@ evas_gl_common_filter_blur_push(Evas_Engine_GL_Context *gc,
gc->pipe[pn].shader.filter.map_nearest = EINA_TRUE;
gc->pipe[pn].shader.filter.map_delete = EINA_FALSE;
pw = tex->pt->w;
ph = tex->pt->h;
ox1 = sx;
oy1 = sy;
ox2 = sx + sw;

View File

@ -81,6 +81,36 @@ _gaussian_interpolate(double **weights, double **offsets, double radius)
return count;
}
static inline Eina_Rectangle
_rect(int x, int y, int w, int h, int maxw, int maxh)
{
Eina_Rectangle rect;
if (x < 0)
{
w -= (-x);
x = 0;
}
if (y < 0)
{
h -= (-y);
y = 0;
}
if ((x + w) > maxw) w = maxw - x;
if ((y + h) > maxh) h = maxh - y;
if (w < 0) w = 0;
if (h < 0) h = 0;
rect.x = x;
rect.y = y;
rect.w = w;
rect.h = h;
return rect;
}
#define S_RECT(_x, _y, _w, _h) _rect(_x, _y, _w, _h, s_w, s_h)
#define D_RECT(_x, _y, _w, _h) _rect(_x, _y, _w, _h, d_w, d_h)
static Eina_Bool
_gl_filter_blur(Render_Engine_GL_Generic *re, Evas_Filter_Command *cmd)
{
@ -89,11 +119,19 @@ _gl_filter_blur(Render_Engine_GL_Generic *re, Evas_Filter_Command *cmd)
RGBA_Draw_Context *dc_save;
Eina_Bool horiz;
double sx, sy, sw, sh, ssx, ssy, ssw, ssh, dx, dy, dw, dh, radius;
int nx, ny, nw, nh, count = 0;
double s_w, s_h, d_w, d_h;
Eina_Rectangle s_ob, d_ob, s_region[4], d_region[4];
int nx, ny, nw, nh, regions, count = 0;
double *weights, *offsets;
DEBUG_TIME_BEGIN();
s_w = cmd->input->w;
s_h = cmd->input->h;
d_w = cmd->output->w;
d_h = cmd->output->h;
EINA_SAFETY_ON_FALSE_RETURN_VAL(s_w && s_h && d_w && d_h, EINA_FALSE);
re->window_use(re->software.ob);
gc = re->window_gl_context_get(re->software.ob);
@ -121,22 +159,6 @@ _gl_filter_blur(Render_Engine_GL_Generic *re, Evas_Filter_Command *cmd)
cmd->output->id, cmd->output->buffer,
radius, horiz ? "X" : "Y");
sx = 0;
sy = 0;
sw = cmd->input->w;
sh = cmd->input->h;
dx = cmd->draw.ox;
dy = cmd->draw.oy;
dw = cmd->output->w;
dh = cmd->output->h;
nx = dx; ny = dy; nw = dw; nh = dh;
RECTS_CLIP_TO_RECT(nx, ny, nw, nh, 0, 0, cmd->output->w, cmd->output->h);
ssx = (double)sx + ((double)(sw * (nx - dx)) / (double)(dw));
ssy = (double)sy + ((double)(sh * (ny - dy)) / (double)(dh));
ssw = ((double)sw * (double)(nw)) / (double)(dw);
ssh = ((double)sh * (double)(nh)) / (double)(dh);
dc_save = gc->dc;
gc->dc = evas_common_draw_context_new();
evas_common_draw_context_set_multiplier(gc->dc, cmd->draw.R, cmd->draw.G, cmd->draw.B, cmd->draw.A);
@ -148,8 +170,68 @@ _gl_filter_blur(Render_Engine_GL_Generic *re, Evas_Filter_Command *cmd)
gc->dc->render_op = _gfx_to_evas_render_op(cmd->draw.rop);
count = _gaussian_interpolate(&weights, &offsets, radius);
evas_gl_common_filter_blur_push(gc, image->tex, ssx, ssy, ssw, ssh, dx, dy, dw, dh,
weights, offsets, count, radius, horiz);
d_ob = cmd->ctx->obscured.effective;
s_ob.x = d_ob.x * s_w / d_w;
s_ob.y = d_ob.y * s_h / d_h;
s_ob.w = d_ob.w * s_w / d_w;
s_ob.h = d_ob.h * s_h / d_h;
if (!d_ob.w || !d_ob.h)
{
s_region[0] = S_RECT(0, 0, s_w, s_h);
d_region[0] = D_RECT(0, 0, d_w, d_h);
regions = 1;
}
else if (horiz)
{
// top (full), left, right, bottom (full)
s_region[0] = S_RECT(0, 0, s_w, s_ob.y);
d_region[0] = D_RECT(0, 0, d_w, d_ob.y);
s_region[1] = S_RECT(0, s_ob.y, s_ob.x, s_ob.h);
d_region[1] = D_RECT(0, d_ob.y, d_ob.x, d_ob.h);
s_region[2] = S_RECT(s_ob.x + s_ob.w, s_ob.y, s_w - s_ob.x - s_ob.w, s_ob.h);
d_region[2] = D_RECT(d_ob.x + d_ob.w, d_ob.y, d_w - d_ob.x - d_ob.w, d_ob.h);
s_region[3] = S_RECT(0, s_ob.y + s_ob.h, s_w, s_h - s_ob.y - s_ob.h);
d_region[3] = D_RECT(0, d_ob.y + d_ob.h, d_w, d_h - d_ob.y - d_ob.h);
regions = 4;
}
else
{
// left (full), top, bottom, right (full)
s_region[0] = S_RECT(0, 0, s_ob.x, s_h);
d_region[0] = D_RECT(0, 0, d_ob.x, d_h);
s_region[1] = S_RECT(s_ob.x, 0, s_ob.w, s_ob.y);
d_region[1] = D_RECT(d_ob.x, 0, d_ob.w, d_ob.y);
s_region[2] = S_RECT(s_ob.x, s_ob.y + s_ob.h, s_ob.w, s_h - s_ob.y - s_ob.h);
d_region[2] = D_RECT(d_ob.x, d_ob.y + d_ob.h, d_ob.w, d_h - d_ob.y - d_ob.h);
s_region[3] = S_RECT(s_ob.x + s_ob.w, 0, s_w - s_ob.x - s_ob.w, s_h);
d_region[3] = D_RECT(d_ob.x + d_ob.w, 0, d_w - d_ob.x - d_ob.w, d_h);
regions = 4;
}
for (int k = 0; k < regions; k++)
{
sx = s_region[k].x;
sy = s_region[k].y;
sw = s_region[k].w;
sh = s_region[k].h;
dx = d_region[k].x + cmd->draw.ox;
dy = d_region[k].y + cmd->draw.oy;
dw = d_region[k].w;
dh = d_region[k].h;
nx = dx; ny = dy; nw = dw; nh = dh;
RECTS_CLIP_TO_RECT(nx, ny, nw, nh, 0, 0, d_w, d_h);
ssx = (double)sx + ((double)(sw * (nx - dx)) / (double)(dw));
ssy = (double)sy + ((double)(sh * (ny - dy)) / (double)(dh));
ssw = ((double)sw * (double)(nw)) / (double)(dw);
ssh = ((double)sh * (double)(nh)) / (double)(dh);
evas_gl_common_filter_blur_push(gc, image->tex, ssx, ssy, ssw, ssh, dx, dy, dw, dh,
weights, offsets, count, radius, horiz);
}
free(weights);
free(offsets);