From da85de48a9d078eeb5a437482c41c85884496318 Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Wed, 20 Mar 2013 17:29:45 +0800 Subject: [PATCH] Feature #69: GLX: Blur background - GLX backend: Add blur background support using a GLSL shader. Only tested with nvidia-drivers-313.26. Known to cause quite some decrease in performance (~10%?). - Detach shaders in glx_create_program(). Misc changes. --- common.h | 24 ++++++++++++ compton.c | 98 ++++++++++++++++++++++++++-------------------- opengl.c | 113 ++++++++++++++++++++++++++++++++++++++++++------------ 3 files changed, 170 insertions(+), 65 deletions(-) diff --git a/common.h b/common.h index 978a9ecb8..977643b60 100644 --- a/common.h +++ b/common.h @@ -626,6 +626,18 @@ typedef struct { f_ReleaseTexImageEXT glXReleaseTexImageProc; /// FBConfig-s for GLX pixmap of different depths. glx_fbconfig_t *glx_fbconfigs[OPENGL_MAX_DEPTH + 1]; +#ifdef CONFIG_VSYNC_OPENGL_GLSL + /// Fragment shader for blur. + GLuint glx_frag_shader_blur; + /// GLSL program for blur. + GLuint glx_prog_blur; + /// Location of uniform "offset_x" in blur GLSL program. + GLint glx_prog_blur_unifm_offset_x; + /// Location of uniform "offset_y" in blur GLSL program. + GLint glx_prog_blur_unifm_offset_y; + /// Location of uniform "factor_center" in blur GLSL program. + GLint glx_prog_blur_unifm_factor_center; +#endif #endif // === X extension related === @@ -1600,10 +1612,22 @@ glx_tex_binded(const glx_texture_t *ptex, Pixmap pixmap) { void glx_set_clip(session_t *ps, XserverRegion reg); +bool +glx_blur_dst(session_t *ps, int dx, int dy, int width, int height, float z, + GLfloat factor_center); + bool glx_render(session_t *ps, const glx_texture_t *ptex, int x, int y, int dx, int dy, int width, int height, int z, double opacity, bool neg, XserverRegion reg_tgt); + +#ifdef CONFIG_VSYNC_OPENGL_GLSL +GLuint +glx_create_shader(GLenum shader_type, const char *shader_str); + +GLuint +glx_create_program(const GLuint * const shaders, int nshaders); +#endif #endif static inline void diff --git a/compton.c b/compton.c index 2b1ca9204..9e8623ea8 100644 --- a/compton.c +++ b/compton.c @@ -1329,60 +1329,76 @@ win_build_picture(session_t *ps, win *w, XRenderPictFormat *pictfmt) { static inline void win_blur_background(session_t *ps, win *w, Picture tgt_buffer, XserverRegion reg_paint) { - const static int convolution_blur_size = 3; - // Convolution filter parameter (box blur) - // gaussian or binomial filters are definitely superior, yet looks - // like they aren't supported as of xorg-server-1.13.0 - XFixed convolution_blur[] = { - // Must convert to XFixed with XDoubleToFixed() - // Matrix size - XDoubleToFixed(convolution_blur_size), - XDoubleToFixed(convolution_blur_size), - // Matrix - XDoubleToFixed(1), XDoubleToFixed(1), XDoubleToFixed(1), - XDoubleToFixed(1), XDoubleToFixed(1), XDoubleToFixed(1), - XDoubleToFixed(1), XDoubleToFixed(1), XDoubleToFixed(1), - }; - const int x = w->a.x; const int y = w->a.y; const int wid = w->widthb; const int hei = w->heightb; - // Directly copying from tgt_buffer does not work, so we create a - // Picture in the middle. - Picture tmp_picture = win_build_picture(ps, w, NULL); - - if (!tmp_picture) - return; - + double factor_center = 1.0; // Adjust blur strength according to window opacity, to make it appear // better during fading if (!ps->o.blur_background_fixed) { double pct = 1.0 - get_opacity_percent(w) * (1.0 - 1.0 / 9.0); - convolution_blur[2 + convolution_blur_size + ((convolution_blur_size - 1) / 2)] = XDoubleToFixed(pct * 8.0 / (1.1 - pct)); + factor_center = pct * 8.0 / (1.1 - pct); } - // Minimize the region we try to blur, if the window itself is not - // opaque, only the frame is. - if (WMODE_SOLID == w->mode && w->frame_opacity) { - XserverRegion reg_all = border_size(ps, w, false); - XserverRegion reg_noframe = win_get_region_noframe(ps, w, false); - XFixesSubtractRegion(ps->dpy, reg_noframe, reg_all, reg_noframe); - XFixesSetPictureClipRegion(ps->dpy, tmp_picture, reg_noframe, 0, 0); - free_region(ps, ®_all); - free_region(ps, ®_noframe); - } + switch (ps->o.backend) { + case BKEND_XRENDER: + { + const static int convolution_blur_size = 3; + // Convolution filter parameter (box blur) + // gaussian or binomial filters are definitely superior, yet looks + // like they aren't supported as of xorg-server-1.13.0 + XFixed convolution_blur[] = { + // Must convert to XFixed with XDoubleToFixed() + // Matrix size + XDoubleToFixed(convolution_blur_size), + XDoubleToFixed(convolution_blur_size), + // Matrix + XDoubleToFixed(1), XDoubleToFixed(1), XDoubleToFixed(1), + XDoubleToFixed(1), XDoubleToFixed(1), XDoubleToFixed(1), + XDoubleToFixed(1), XDoubleToFixed(1), XDoubleToFixed(1), + }; + + // Directly copying from tgt_buffer does not work, so we create a + // Picture in the middle. + Picture tmp_picture = win_build_picture(ps, w, NULL); + + if (!tmp_picture) + return; - // Copy the content to tmp_picture, then copy back. The filter must - // be applied on tgt_buffer, to get the nearby pixels outside the - // window. - XRenderSetPictureFilter(ps->dpy, tgt_buffer, XRFILTER_CONVOLUTION, (XFixed *) convolution_blur, sizeof(convolution_blur) / sizeof(XFixed)); - XRenderComposite(ps->dpy, PictOpSrc, tgt_buffer, None, tmp_picture, x, y, 0, 0, 0, 0, wid, hei); - xrfilter_reset(ps, tgt_buffer); - XRenderComposite(ps->dpy, PictOpSrc, tmp_picture, None, tgt_buffer, 0, 0, 0, 0, x, y, wid, hei); + convolution_blur[2 + convolution_blur_size + ((convolution_blur_size - 1) / 2)] = XDoubleToFixed(factor_center); - free_picture(ps, &tmp_picture); + // Minimize the region we try to blur, if the window itself is not + // opaque, only the frame is. + if (WMODE_SOLID == w->mode && w->frame_opacity) { + XserverRegion reg_all = border_size(ps, w, false); + XserverRegion reg_noframe = win_get_region_noframe(ps, w, false); + XFixesSubtractRegion(ps->dpy, reg_noframe, reg_all, reg_noframe); + XFixesSetPictureClipRegion(ps->dpy, tmp_picture, reg_noframe, 0, 0); + free_region(ps, ®_all); + free_region(ps, ®_noframe); + } + + // Copy the content to tmp_picture, then copy back. The filter must + // be applied on tgt_buffer, to get the nearby pixels outside the + // window. + XRenderSetPictureFilter(ps->dpy, tgt_buffer, XRFILTER_CONVOLUTION, (XFixed *) convolution_blur, sizeof(convolution_blur) / sizeof(XFixed)); + XRenderComposite(ps->dpy, PictOpSrc, tgt_buffer, None, tmp_picture, x, y, 0, 0, 0, 0, wid, hei); + xrfilter_reset(ps, tgt_buffer); + XRenderComposite(ps->dpy, PictOpSrc, tmp_picture, None, tgt_buffer, 0, 0, 0, 0, x, y, wid, hei); + + free_picture(ps, &tmp_picture); + } + break; +#ifdef CONFIG_VSYNC_OPENGL + case BKEND_GLX: + glx_blur_dst(ps, x, y, wid, hei, ps->glx_z - 0.5, factor_center); + break; +#endif + default: + assert(0); + } } static void diff --git a/opengl.c b/opengl.c index eca91667b..a0bed63bb 100644 --- a/opengl.c +++ b/opengl.c @@ -140,6 +140,14 @@ glx_init_end: */ void glx_destroy(session_t *ps) { +#ifdef CONFIG_VSYNC_OPENGL_GLSL + // Free GLSL shaders/programs + if (ps->glx_frag_shader_blur) + glDeleteShader(ps->glx_frag_shader_blur); + if (ps->glx_prog_blur) + glDeleteProgram(ps->glx_prog_blur); +#endif + // Free FBConfigs for (int i = 0; i <= OPENGL_MAX_DEPTH; ++i) { free(ps->glx_fbconfigs[i]); @@ -173,20 +181,66 @@ glx_on_root_change(session_t *ps) { */ bool glx_init_blur(session_t *ps) { - printf_errf("(): Blur on GLX backend isn't implemented yet, sorry."); - return false; +#ifdef CONFIG_VSYNC_OPENGL_GLSL + if (ps->o.glx_no_stencil) { + printf_errf("(): I'm afraid blur background won't work so well without " + "stencil buffer support."); + return false; + } + + // Build shader + static const char *FRAG_SHADER_BLUR = + "#version 110\n" + "uniform float offset_x;\n" + "uniform float offset_y;\n" + "uniform float factor_center;\n" + "uniform sampler2D tex_scr;\n" + "\n" + "void main() {\n" + " vec4 sum = vec4(0.0, 0.0, 0.0, 0.0);\n" + " sum += texture2D(tex_scr, vec2(gl_TexCoord[0].x - offset_x, gl_TexCoord[0].y - offset_y));\n" + " sum += texture2D(tex_scr, vec2(gl_TexCoord[0].x - offset_x, gl_TexCoord[0].y));\n" + " sum += texture2D(tex_scr, vec2(gl_TexCoord[0].x - offset_x, gl_TexCoord[0].y + offset_y));\n" + " sum += texture2D(tex_scr, vec2(gl_TexCoord[0].x, gl_TexCoord[0].y - offset_y));\n" + " sum += texture2D(tex_scr, vec2(gl_TexCoord[0].x, gl_TexCoord[0].y)) * factor_center;\n" + " sum += texture2D(tex_scr, vec2(gl_TexCoord[0].x, gl_TexCoord[0].y + offset_y));\n" + " sum += texture2D(tex_scr, vec2(gl_TexCoord[0].x + offset_x, gl_TexCoord[0].y - offset_y));\n" + " sum += texture2D(tex_scr, vec2(gl_TexCoord[0].x + offset_x, gl_TexCoord[0].y));\n" + " sum += texture2D(tex_scr, vec2(gl_TexCoord[0].x + offset_x, gl_TexCoord[0].y + offset_y));\n" + " gl_FragColor = sum / (factor_center + 8.0);\n" + "}\n" + ; + ps->glx_frag_shader_blur = glx_create_shader(GL_FRAGMENT_SHADER, FRAG_SHADER_BLUR); + if (!ps->glx_frag_shader_blur) { + printf_errf("(): Failed to create fragment shader."); + return false; + } + + ps->glx_prog_blur = glx_create_program(&ps->glx_frag_shader_blur, 1); + if (!ps->glx_prog_blur) { + printf_errf("(): Failed to create GLSL program."); + return false; + } + +#define P_GET_UNIFM_LOC(name, target) { \ + ps->target = glGetUniformLocation(ps->glx_prog_blur, name); \ + if (ps->target < 0) { \ + printf_errf("(): Failed to get location of uniform '" name "'."); \ + return false; \ + } \ +} + + P_GET_UNIFM_LOC("factor_center", glx_prog_blur_unifm_factor_center); + P_GET_UNIFM_LOC("offset_x", glx_prog_blur_unifm_offset_x); + P_GET_UNIFM_LOC("offset_y", glx_prog_blur_unifm_offset_y); -// #ifdef CONFIG_VSYNC_OPENGL_GLSL -// static const char *FRAG_SHADER_BLUR = ""; -// GLuint frag_shader = glx_create_shader(GL_FRAGMENT_SHADER, FRAG_SHADER_BLUR); -// if (!frag_shader) { -// printf_errf("(): Failed to create fragment shader for blurring."); -// return false; -// } -// #else -// printf_errf("(): GLSL support not compiled in. Cannot do blur with GLX backend."); -// return false; -// #endif +#undef P_GET_UNIFM_LOC + + return true; +#else + printf_errf("(): GLSL support not compiled in. Cannot do blur with GLX backend."); + return false; +#endif } /** @@ -590,7 +644,8 @@ glx_set_clip(session_t *ps, XserverRegion reg) { } bool -glx_blur_dst(session_t *ps, int dx, int dy, int width, int height, float z) { +glx_blur_dst(session_t *ps, int dx, int dy, int width, int height, float z, + GLfloat factor_center) { // Read destination pixels into a texture GLuint tex_scr = 0; glGenTextures(1, &tex_scr); @@ -617,10 +672,18 @@ glx_blur_dst(session_t *ps, int dx, int dy, int width, int height, float z) { #endif // Paint it back - // TODO: Blur function. We are using color negation for testing now. - glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); - glTexEnvf(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_REPLACE); - glTexEnvf(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_ONE_MINUS_SRC_COLOR); + // Color negation for testing... + // glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); + // glTexEnvf(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_REPLACE); + // glTexEnvf(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_ONE_MINUS_SRC_COLOR); + + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); +#ifdef CONFIG_VSYNC_OPENGL_GLSL + glUseProgram(ps->glx_prog_blur); + glUniform1f(ps->glx_prog_blur_unifm_offset_x, 1.0f / width); + glUniform1f(ps->glx_prog_blur_unifm_offset_y, 1.0f / height); + glUniform1f(ps->glx_prog_blur_unifm_factor_center, factor_center); +#endif glBegin(GL_QUADS); @@ -653,6 +716,9 @@ glx_blur_dst(session_t *ps, int dx, int dy, int width, int height, float z) { glEnd(); +#ifdef CONFIG_VSYNC_OPENGL_GLSL + glUseProgram(0); +#endif glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); glBindTexture(tex_tgt, 0); glDeleteTextures(1, &tex_scr); @@ -668,8 +734,6 @@ bool glx_render(session_t *ps, const glx_texture_t *ptex, int x, int y, int dx, int dy, int width, int height, int z, double opacity, bool neg, XserverRegion reg_tgt) { - bool blur_background = false; - if (!ptex || !ptex->texture) { printf_errf("(): Missing texture."); return false; @@ -678,8 +742,6 @@ glx_render(session_t *ps, const glx_texture_t *ptex, // Enable blending if needed if (opacity < 1.0 || GLX_TEXTURE_FORMAT_RGBA_EXT == ps->glx_fbconfigs[ptex->depth]->texture_fmt) { - if (!ps->o.glx_no_stencil && blur_background) - glx_blur_dst(ps, dx, dy, width, height, z - 0.5); glEnable(GL_BLEND); @@ -832,8 +894,7 @@ glx_create_shader_end: } GLuint -glx_create_program(GLenum shader_type, const GLuint * const shaders, - int nshaders) { +glx_create_program(const GLuint * const shaders, int nshaders) { bool success = false; GLuint program = glCreateProgram(); if (!program) { @@ -863,6 +924,10 @@ glx_create_program(GLenum shader_type, const GLuint * const shaders, success = true; glx_create_program_end: + if (program) { + for (int i = 0; i < nshaders; ++i) + glDetachShader(program, shaders[i]); + } if (program && !success) { glDeleteProgram(program); program = 0;