From 1bdd035974a0166434cdb2dd5d34405d6ddf184d Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Mon, 20 May 2013 18:04:40 +0800 Subject: [PATCH] Imp: Multi-pass blur & D-Bus fading control - Add multipass blur support. Note GLX Framebuffer support is required. My benchmark shows multipass blur brings 5% performance boost for X Render backend (3x3box). On GLX backend it brings 10% performance boost for 5x5box but negatively affects performance for 3x3box. Thanks to jrfonseca for advice. (#107) - GLX backend: Cache blur texture for each window, for a 12% performance boost. - Add D-Bus fading control. Thanks to yulan6248 for testing. (#112) - Fix FAQ link in README.md. Thanks to lorenzos for report. (#111) - Correctly deinitialize VSync on on-the-fly VSync method switch. - X Render backend: Normalize blur kernel. - Code clean-up. - Known issue: Linear corruption on border of a window may appear with X Render multi-pass blur. Possible to fix but probably not worthwhile. --- common.h | 172 +++++++++++++++++----- compton.c | 313 ++++++++++++++++++++++++++++++++-------- compton.h | 27 ++++ dbus.c | 53 ++++++- opengl.c | 424 ++++++++++++++++++++++++++++++++++++++---------------- 5 files changed, 765 insertions(+), 224 deletions(-) diff --git a/common.h b/common.h index bf9f8ae01..cab29f76a 100644 --- a/common.h +++ b/common.h @@ -26,6 +26,10 @@ // #define DEBUG_FRAME 1 // #define DEBUG_LEADER 1 // #define DEBUG_C2 1 +// #define DEBUG_GLX 1 +// #define DEBUG_GLX_GLSL 1 +// #define DEBUG_GLX_ERR 1 +// #define DEBUG_GLX_MARK 1 // #define MONITOR_REPAINT 1 // Whether to enable PCRE regular expression support in blacklists, enabled @@ -40,8 +44,12 @@ // #define CONFIG_LIBCONFIG_LEGACY 1 // Whether to enable DRM VSync support // #define CONFIG_VSYNC_DRM 1 -// Whether to enable OpenGL VSync support +// Whether to enable OpenGL support // #define CONFIG_VSYNC_OPENGL 1 +// Whether to enable GLX GLSL support +// #define CONFIG_VSYNC_OPENGL_GLSL 1 +// Whether to enable GLX FBO support +// #define CONFIG_VSYNC_OPENGL_FBO 1 // Whether to enable DBus support with libdbus. // #define CONFIG_DBUS 1 // Whether to enable condition support. @@ -96,7 +104,7 @@ // libGL #ifdef CONFIG_VSYNC_OPENGL -#ifdef CONFIG_VSYNC_OPENGL_GLSL +#if defined(CONFIG_VSYNC_OPENGL_GLSL) || defined(CONFIG_VSYNC_OPENGL_FBO) #define GL_GLEXT_PROTOTYPES #endif @@ -183,6 +191,9 @@ /// @brief Maximum OpenGL buffer age. #define CGLX_MAX_BUFFER_AGE 5 +/// @brief Maximum passes for blur. +#define MAX_BLUR_PASS 5 + // Window flags // Window size is changed @@ -343,6 +354,32 @@ struct _glx_texture { }; #endif +#ifdef CONFIG_VSYNC_OPENGL_GLSL +typedef struct { + /// Fragment shader for blur. + GLuint frag_shader; + /// GLSL program for blur. + GLuint prog; + /// Location of uniform "offset_x" in blur GLSL program. + GLint unifm_offset_x; + /// Location of uniform "offset_y" in blur GLSL program. + GLint unifm_offset_y; + /// Location of uniform "factor_center" in blur GLSL program. + GLint unifm_factor_center; +} glx_blur_pass_t; + +typedef struct { + /// Framebuffer used for blurring. + GLuint fbo; + /// Textures used for blurring. + GLuint textures[2]; + /// Width of the textures. + int width; + /// Height of the textures. + int height; +} glx_blur_cache_t; +#endif + typedef struct { Pixmap pixmap; Picture pict; @@ -501,7 +538,7 @@ typedef struct { /// Background blur blacklist. A linked list of conditions. c2_lptr_t *blur_background_blacklist; /// Blur convolution kernel. - XFixed *blur_kern; + XFixed *blur_kerns[MAX_BLUR_PASS]; /// How much to dim an inactive window. 0.0 - 1.0, 0 to disable. double inactive_dim; /// Whether to use fixed inactive dim opacity, instead of deciding @@ -614,6 +651,8 @@ typedef struct { /// Current GLX Z value. int glx_z; #endif + // Cached blur convolution kernels. + XFixed *blur_kerns_cache[MAX_BLUR_PASS]; /// Reset program after next paint. bool reset; @@ -702,16 +741,7 @@ typedef struct { /// 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; + glx_blur_pass_t glx_blur_passes[MAX_BLUR_PASS]; #endif #endif @@ -908,6 +938,8 @@ typedef struct _win { /// Do not fade if it's false. Change on window type change. /// Used by fading blacklist in the future. bool fade; + /// Override value of window fade state. Set by D-Bus method calls. + switch_t fade_force; /// Callback to be called after fading completed. void (*fade_callback) (session_t *ps, struct _win *w); @@ -950,6 +982,11 @@ typedef struct _win { /// Whether to blur window background. bool blur_background; + +#ifdef CONFIG_VSYNC_OPENGL_GLSL + /// Textures and FBO background blur use. + glx_blur_cache_t glx_blur_cache; +#endif } win; /// Temporary structure used for communication between @@ -1749,6 +1786,9 @@ force_repaint(session_t *ps); bool vsync_init(session_t *ps); +void +vsync_deinit(session_t *ps); + #ifdef CONFIG_VSYNC_OPENGL /** @name GLX */ @@ -1788,9 +1828,13 @@ glx_tex_binded(const glx_texture_t *ptex, Pixmap pixmap) { void glx_set_clip(session_t *ps, XserverRegion reg, const reg_data_t *pcache_reg); +#ifdef CONFIG_VSYNC_OPENGL_GLSL bool glx_blur_dst(session_t *ps, int dx, int dy, int width, int height, float z, - GLfloat factor_center, XserverRegion reg_tgt, const reg_data_t *pcache_reg); + GLfloat factor_center, + XserverRegion reg_tgt, const reg_data_t *pcache_reg, + glx_blur_cache_t *pbc); +#endif bool glx_dim_dst(session_t *ps, int dx, int dy, int width, int height, float z, @@ -1812,7 +1856,78 @@ glx_create_shader(GLenum shader_type, const char *shader_str); GLuint glx_create_program(const GLuint * const shaders, int nshaders); #endif + +/** + * Free a GLX texture. + */ +static inline void +free_texture_r(session_t *ps, GLuint *ptexture) { + if (*ptexture) { + assert(ps->glx_context); + glDeleteTextures(1, ptexture); + *ptexture = 0; + } +} + +/** + * Free a GLX Framebuffer object. + */ +static inline void +free_glx_fbo(session_t *ps, GLuint *pfbo) { +#ifdef CONFIG_VSYNC_OPENGL_FBO + if (*pfbo) { + glDeleteFramebuffers(1, pfbo); + *pfbo = 0; + } #endif + assert(!*pfbo); +} + +#ifdef CONFIG_VSYNC_OPENGL_GLSL +/** + * Free data in glx_blur_cache_t on resize. + */ +static inline void +free_glx_bc_resize(session_t *ps, glx_blur_cache_t *pbc) { + free_texture_r(ps, &pbc->textures[0]); + free_texture_r(ps, &pbc->textures[1]); + pbc->width = 0; + pbc->height = 0; +} + +/** + * Free a glx_blur_cache_t + */ +static inline void +free_glx_bc(session_t *ps, glx_blur_cache_t *pbc) { + free_glx_fbo(ps, &pbc->fbo); + free_glx_bc_resize(ps, pbc); +} +#endif +#endif + +/** + * Free a glx_texture_t. + */ +static inline void +free_texture(session_t *ps, glx_texture_t **pptex) { + glx_texture_t *ptex = *pptex; + + // Quit if there's nothing + if (!ptex) + return; + +#ifdef CONFIG_VSYNC_OPENGL + glx_release_pixmap(ps, ptex); + + free_texture_r(ps, &ptex->texture); + + // Free structure itself + free(ptex); + *pptex = NULL; +#endif + assert(!*pptex); +} /** * Add a OpenGL debugging marker. @@ -1846,29 +1961,6 @@ glx_mark_frame(session_t *ps) { #endif } -static inline void -free_texture(session_t *ps, glx_texture_t **pptex) { -#ifdef CONFIG_VSYNC_OPENGL - glx_texture_t *ptex = *pptex; - - // Quit if there's nothing - if (!ptex) - return; - - glx_release_pixmap(ps, ptex); - - // Free texture - if (ptex->texture) { - glDeleteTextures(1, &ptex->texture); - ptex->texture = 0; - } - - // Free structure itself - free(ptex); - *pptex = NULL; -#endif -} - ///@} /** @name DBus handling @@ -1912,6 +2004,9 @@ cdbus_ev_win_focusin(session_t *ps, win *w); void win_set_shadow_force(session_t *ps, win *w, switch_t val); +void +win_set_fade_force(session_t *ps, win *w, switch_t val); + void win_set_focused_force(session_t *ps, win *w, switch_t val); @@ -1920,6 +2015,9 @@ win_set_invert_color_force(session_t *ps, win *w, switch_t val); void opts_init_track_focus(session_t *ps); + +void +opts_set_no_fading_openclose(session_t *ps, bool newval); //!@} #endif diff --git a/compton.c b/compton.c index 7e005b638..f058bf927 100644 --- a/compton.c +++ b/compton.c @@ -70,6 +70,14 @@ static int (* const (VSYNC_FUNCS_WAIT[NUM_VSYNC]))(session_t *ps) = { #endif }; +/// Function pointers to deinitialize VSync. +static void (* const (VSYNC_FUNCS_DEINIT[NUM_VSYNC]))(session_t *ps) = { +#ifdef CONFIG_VSYNC_OPENGL + [VSYNC_OPENGL_SWC ] = vsync_opengl_swc_deinit, + [VSYNC_OPENGL_MSWC ] = vsync_opengl_mswc_deinit, +#endif +}; + /// Names of root window properties that could point to a pixmap of /// background. const static char *background_props_str[] = { @@ -1276,13 +1284,11 @@ win_paint_shadow(session_t *ps, win *w, } /** - * Create an alternative picture for a window. + * Create an picture. */ static inline Picture -win_build_picture(session_t *ps, win *w, XRenderPictFormat *pictfmt) { - const int wid = w->widthb; - const int hei = w->heightb; - +xr_build_picture(session_t *ps, int wid, int hei, + XRenderPictFormat *pictfmt) { if (!pictfmt) pictfmt = XRenderFindVisualFormat(ps->dpy, ps->vis); @@ -1299,6 +1305,73 @@ win_build_picture(session_t *ps, win *w, XRenderPictFormat *pictfmt) { return tmp_picture; } +/** + * @brief Blur an area on a buffer. + * + * @param ps current session + * @param tgt_buffer a buffer as both source and destination + * @param x x pos + * @param y y pos + * @param wid width + * @param hei height + * @param blur_kerns blur kernels, ending with a NULL, guaranteed to have at + * least one kernel + * @param reg_clip a clipping region to be applied on intermediate buffers + * + * @return true if successful, false otherwise + */ +static bool +xr_blur_dst(session_t *ps, Picture tgt_buffer, + int x, int y, int wid, int hei, XFixed **blur_kerns, + XserverRegion reg_clip) { + assert(blur_kerns[0]); + + // Directly copying from tgt_buffer to it does not work, so we create a + // Picture in the middle. + Picture tmp_picture = xr_build_picture(ps, wid, hei, NULL); + + if (!tmp_picture) { + printf_errf("(): Failed to build intermediate Picture."); + return false; + } + + if (reg_clip && tmp_picture) + XFixesSetPictureClipRegion(ps->dpy, tmp_picture, reg_clip, 0, 0); + + Picture src_pict = tgt_buffer, dst_pict = tmp_picture; + for (int i = 0; blur_kerns[i]; ++i) { + assert(i < MAX_BLUR_PASS - 1); + XFixed *convolution_blur = blur_kerns[i]; + int kwid = XFixedToDouble(convolution_blur[0]), + khei = XFixedToDouble(convolution_blur[1]); + bool rd_from_tgt = (tgt_buffer == src_pict); + + // Copy from source picture to destination. The filter must + // be applied on source picture, to get the nearby pixels outside the + // window. + XRenderSetPictureFilter(ps->dpy, src_pict, XRFILTER_CONVOLUTION, + convolution_blur, kwid * khei + 2); + XRenderComposite(ps->dpy, PictOpSrc, src_pict, None, dst_pict, + (rd_from_tgt ? x: 0), (rd_from_tgt ? y: 0), 0, 0, + (rd_from_tgt ? 0: x), (rd_from_tgt ? 0: y), wid, hei); + xrfilter_reset(ps, src_pict); + + { + XserverRegion tmp = src_pict; + src_pict = dst_pict; + dst_pict = tmp; + } + } + + if (src_pict != tgt_buffer) + XRenderComposite(ps->dpy, PictOpSrc, src_pict, None, tgt_buffer, + 0, 0, 0, 0, x, y, wid, hei); + + free_picture(ps, &tmp_picture); + + return true; +} + /** * Blur the background of a window. */ @@ -1321,46 +1394,63 @@ win_blur_background(session_t *ps, win *w, Picture tgt_buffer, switch (ps->o.backend) { case BKEND_XRENDER: { - // 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); + // Normalize blur kernels + for (int i = 0; i < MAX_BLUR_PASS; ++i) { + XFixed *kern_src = ps->o.blur_kerns[i]; + XFixed *kern_dst = ps->blur_kerns_cache[i]; + assert(i < MAX_BLUR_PASS); + if (!kern_src) { + assert(!kern_dst); + break; + } + + assert(!kern_dst + || (kern_src[0] == kern_dst[0] && kern_src[1] == kern_dst[1])); + + // Skip for fixed factor_center if the cache exists already + if (ps->o.blur_background_fixed && kern_dst) continue; + + int kwid = XFixedToDouble(kern_src[0]), + khei = XFixedToDouble(kern_src[1]); - if (!tmp_picture) - return; + // Allocate cache space if needed + if (!kern_dst) { + kern_dst = malloc((kwid * khei + 2) * sizeof(XFixed)); + if (!kern_dst) { + printf_errf("(): Failed to allocate memory for blur kernel."); + return; + } + ps->blur_kerns_cache[i] = kern_dst; + } - XFixed *convolution_blur = ps->o.blur_kern; - int kwid = XFixedToDouble((ps->o.blur_kern[0])), - khei = XFixedToDouble((ps->o.blur_kern[1])); + // Modify the factor of the center pixel + kern_src[2 + (khei / 2) * kwid + kwid / 2] = + XDoubleToFixed(factor_center); - // Modify the factor of the center pixel - convolution_blur[2 + (khei / 2) * kwid + kwid / 2] = XDoubleToFixed(factor_center); + // Copy over + memcpy(kern_dst, kern_src, (kwid * khei + 2) * sizeof(XFixed)); + normalize_conv_kern(kwid, khei, kern_dst + 2); + } // 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_noframe = None; + if (WMODE_SOLID == w->mode) { XserverRegion reg_all = border_size(ps, w, false); - XserverRegion reg_noframe = win_get_region_noframe(ps, w, false); + 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, convolution_blur, kwid * khei + 2); - 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); + xr_blur_dst(ps, tgt_buffer, x, y, wid, hei, ps->blur_kerns_cache, + reg_noframe); + free_region(ps, ®_noframe); } break; -#ifdef CONFIG_VSYNC_OPENGL +#ifdef CONFIG_VSYNC_OPENGL_GLSL case BKEND_GLX: + // TODO: Handle frame opacity glx_blur_dst(ps, x, y, wid, hei, ps->glx_z - 0.5, factor_center, - reg_paint, pcache_reg); + reg_paint, pcache_reg, &w->glx_blur_cache); break; #endif default: @@ -1447,7 +1537,7 @@ win_paint_win(session_t *ps, win *w, XserverRegion reg_paint, // Invert window color, if required if (BKEND_XRENDER == ps->o.backend && w->invert_color) { - Picture newpict = win_build_picture(ps, w, w->pictfmt); + Picture newpict = xr_build_picture(ps, wid, hei, w->pictfmt); if (newpict) { // Apply clipping region to save some CPU if (reg_paint) { @@ -1993,14 +2083,8 @@ map_win(session_t *ps, Window id) { win_determine_shadow(ps, w); // Set fading state - w->in_openclose = false; - if (ps->o.no_fading_openclose) { - set_fade_callback(ps, w, finish_map_win, true); - w->in_openclose = true; - } - else { - set_fade_callback(ps, w, NULL, true); - } + w->in_openclose = true; + set_fade_callback(ps, w, finish_map_win, true); win_determine_fade(ps, w); win_determine_blur_background(ps, w); @@ -2066,9 +2150,7 @@ unmap_win(session_t *ps, win *w) { // Fading out w->flags |= WFLAG_OPCT_CHANGE; set_fade_callback(ps, w, unmap_callback, false); - if (ps->o.no_fading_openclose) { - w->in_openclose = true; - } + w->in_openclose = true; win_determine_fade(ps, w); // Validate pixmap if we have to do fading @@ -2195,7 +2277,9 @@ calc_dim(session_t *ps, win *w) { */ static void win_determine_fade(session_t *ps, win *w) { - if ((ps->o.no_fading_openclose && w->in_openclose) + if (UNSET != w->fade_force) + w->fade = w->fade_force; + else if ((ps->o.no_fading_openclose && w->in_openclose) || win_match(ps, w, ps->o.fade_blacklist, &w->cache_fblst)) w->fade = false; else @@ -2593,6 +2677,7 @@ add_win(session_t *ps, Window id, Window prev) { .opacity_prop_client = OPAQUE, .fade = false, + .fade_force = UNSET, .fade_callback = NULL, .frame_opacity = 0.0, @@ -3418,6 +3503,19 @@ win_set_shadow_force(session_t *ps, win *w, switch_t val) { if (val != w->shadow_force) { w->shadow_force = val; win_determine_shadow(ps, w); + ps->ev_received = true; + } +} + +/** + * Set w->fade_force of a window. + */ +void +win_set_fade_force(session_t *ps, win *w, switch_t val) { + if (val != w->fade_force) { + w->fade_force = val; + win_determine_fade(ps, w); + ps->ev_received = true; } } @@ -3429,6 +3527,7 @@ win_set_focused_force(session_t *ps, win *w, switch_t val) { if (val != w->focused_force) { w->focused_force = val; win_update_focused(ps, w); + ps->ev_received = true; } } @@ -3440,6 +3539,7 @@ win_set_invert_color_force(session_t *ps, win *w, switch_t val) { if (val != w->invert_color_force) { w->invert_color_force = val; win_determine_invert_color(ps, w); + ps->ev_received = true; } } @@ -3465,6 +3565,20 @@ opts_init_track_focus(session_t *ps) { // Recheck focus recheck_focus(ps); } + +/** + * Set no_fading_openclose option. + */ +void +opts_set_no_fading_openclose(session_t *ps, bool newval) { + if (newval != ps->o.no_fading_openclose) { + ps->o.no_fading_openclose = newval; + for (win *w = ps->list; w; w = w->next) + win_determine_fade(ps, w); + ps->ev_received = true; + } +} + //!@} #endif @@ -4222,6 +4336,8 @@ usage(int ret) { " --blur-background-fixed.\n" " A 7x7 Guassian blur kernel looks like:\n" " --blur-kern '7,7,0.000003,0.000102,0.000849,0.001723,0.000849,0.000102,0.000003,0.000102,0.003494,0.029143,0.059106,0.029143,0.003494,0.000102,0.000849,0.029143,0.243117,0.493069,0.243117,0.029143,0.000849,0.001723,0.059106,0.493069,0.493069,0.059106,0.001723,0.000849,0.029143,0.243117,0.493069,0.243117,0.029143,0.000849,0.000102,0.003494,0.029143,0.059106,0.029143,0.003494,0.000102,0.000003,0.000102,0.000849,0.001723,0.000849,0.000102,0.000003'\n" + " Up to 4 blur kernels may be specified, separated with semicolon, for\n" + " multi-pass blur.\n" " May also be one the predefined kernels: 3x3box (default), 5x5box,\n" " 7x7box, 3x3gaussian, 5x5gaussian, 7x7gaussian, 9x9gaussian,\n" " 11x11gaussian.\n" @@ -4419,7 +4535,7 @@ parse_matrix_readnum(const char *src, double *dest) { * Parse a matrix. */ static inline XFixed * -parse_matrix(session_t *ps, const char *src) { +parse_matrix(session_t *ps, const char *src, const char **endptr) { int wid = 0, hei = 0; const char *pc = NULL; XFixed *matrix = NULL; @@ -4481,12 +4597,28 @@ parse_matrix(session_t *ps, const char *src) { } // Detect trailing characters - for ( ;*pc; ++pc) + for ( ;*pc && ';' != *pc; ++pc) if (!isspace(*pc) && ',' != *pc) { printf_errf("(): Trailing characters in matrix string."); goto parse_matrix_err; } + // Jump over spaces after ';' + if (';' == *pc) { + ++pc; + while (*pc && isspace(*pc)) + ++pc; + } + + // Require an end of string if endptr is not provided, otherwise + // copy end pointer to endptr + if (endptr) + *endptr = pc; + else if (*pc) { + printf_errf("(): Only one matrix expected."); + goto parse_matrix_err; + } + // Fill in width and height matrix[0] = XDoubleToFixed(wid); matrix[1] = XDoubleToFixed(hei); @@ -4502,7 +4634,12 @@ parse_matrix_err: * Parse a convolution kernel. */ static inline XFixed * -parse_conv_kern(session_t *ps, const char *src) { +parse_conv_kern(session_t *ps, const char *src, const char **endptr) { + return parse_matrix(ps, src, endptr); +} + +static bool +parse_conv_kern_lst(session_t *ps, const char *src, XFixed **dest, int max) { static const struct { const char *name; const char *kern_str; @@ -4519,8 +4656,30 @@ parse_conv_kern(session_t *ps, const char *src) { for (int i = 0; i < sizeof(CONV_KERN_PREDEF) / sizeof(CONV_KERN_PREDEF[0]); ++i) if (!strcmp(CONV_KERN_PREDEF[i].name, src)) - return parse_matrix(ps, CONV_KERN_PREDEF[i].kern_str); - return parse_matrix(ps, src); + return parse_conv_kern_lst(ps, CONV_KERN_PREDEF[i].kern_str, dest, max); + + int i = 0; + const char *pc = src; + + // Free old kernels + for (i = 0; i < max; ++i) { + free(dest[i]); + dest[i] = NULL; + } + + // Continue parsing until the end of source string + i = 0; + while (pc && *pc && i < max - 1) { + if (!(dest[i++] = parse_conv_kern(ps, pc, &pc))) + return false; + } + + if (*pc) { + printf_errf("(): Too many blur kernels!"); + return false; + } + + return true; } #ifdef CONFIG_LIBCONFIG @@ -5115,9 +5274,9 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { break; case 301: // --blur-kern - free(ps->o.blur_kern); - if (!(ps->o.blur_kern = parse_conv_kern(ps, optarg))) + if (!parse_conv_kern_lst(ps, optarg, ps->o.blur_kerns, MAX_BLUR_PASS)) exit(1); + break; case 302: // --resize-damage ps->o.resize_damage = atoi(optarg); @@ -5187,7 +5346,7 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { } // Fill default blur kernel - if (ps->o.blur_background && !ps->o.blur_kern) { + if (ps->o.blur_background && !ps->o.blur_kerns[0]) { // 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 @@ -5200,12 +5359,12 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { XDoubleToFixed(1), XDoubleToFixed(1), XDoubleToFixed(1), XDoubleToFixed(1), XDoubleToFixed(1), XDoubleToFixed(1), }; - ps->o.blur_kern = malloc(sizeof(convolution_blur)); - if (!ps->o.blur_kern) { + ps->o.blur_kerns[0] = malloc(sizeof(convolution_blur)); + if (!ps->o.blur_kerns[0]) { printf_errf("(): Failed to allocate memory for convolution kernel."); exit(1); } - memcpy(ps->o.blur_kern, &convolution_blur, sizeof(convolution_blur)); + memcpy(ps->o.blur_kerns[0], &convolution_blur, sizeof(convolution_blur)); } } @@ -5536,6 +5695,19 @@ vsync_opengl_oml_wait(session_t *ps) { return 0; } + +static void +vsync_opengl_swc_deinit(session_t *ps) { + // The standard says it doesn't accept 0, but in fact it probably does + if (ps->glx_context && ps->glXSwapIntervalProc) + ps->glXSwapIntervalProc(0); +} + +static void +vsync_opengl_mswc_deinit(session_t *ps) { + if (ps->glx_context && ps->glXSwapIntervalMESAProc) + ps->glXSwapIntervalMESAProc(0); +} #endif /** @@ -5564,6 +5736,16 @@ vsync_wait(session_t *ps) { VSYNC_FUNCS_WAIT[ps->o.vsync](ps); } +/** + * Deinitialize current VSync method. + */ +void +vsync_deinit(session_t *ps) { + if (ps->o.vsync && VSYNC_FUNCS_DEINIT[ps->o.vsync]) + VSYNC_FUNCS_DEINIT[ps->o.vsync](ps); + ps->o.vsync = VSYNC_NONE; +} + /** * Pregenerate alpha pictures. */ @@ -6030,7 +6212,7 @@ session_init(session_t *ps_old, int argc, char **argv) { .blur_background_frame = false, .blur_background_fixed = false, .blur_background_blacklist = NULL, - .blur_kern = NULL, + .blur_kerns = { NULL }, .inactive_dim = 0.0, .inactive_dim_fixed = false, .invert_color_list = NULL, @@ -6096,12 +6278,6 @@ session_init(session_t *ps_old, int argc, char **argv) { .glXWaitVideoSyncSGI = NULL, .glXGetSyncValuesOML = NULL, .glXWaitForMscOML = NULL, - -#ifdef CONFIG_VSYNC_OPENGL_GLSL - .glx_prog_blur_unifm_offset_x = -1, - .glx_prog_blur_unifm_offset_y = -1, - .glx_prog_blur_unifm_factor_center = -1, -#endif #endif .xfixes_event = 0, @@ -6151,6 +6327,14 @@ session_init(session_t *ps_old, int argc, char **argv) { // Allocate a session and copy default values into it session_t *ps = malloc(sizeof(session_t)); memcpy(ps, &s_def, sizeof(session_t)); +#ifdef CONFIG_VSYNC_OPENGL_GLSL + for (int i = 0; i < MAX_BLUR_PASS; ++i) { + glx_blur_pass_t *ppass = &ps->glx_blur_passes[i]; + ppass->unifm_factor_center = -1; + ppass->unifm_offset_x = -1; + ppass->unifm_offset_y = -1; + } +#endif ps_g = ps; ps->ignore_tail = &ps->ignore_head; gettimeofday(&ps->time_start, NULL); @@ -6513,7 +6697,10 @@ session_destroy(session_t *ps) { free(ps->o.display); free(ps->o.logpath); free(ps->o.config_file); - free(ps->o.blur_kern); + for (int i = 0; i < MAX_BLUR_PASS; ++i) { + free(ps->o.blur_kerns[i]); + free(ps->blur_kerns_cache[i]); + } free(ps->pfds_read); free(ps->pfds_write); free(ps->pfds_except); diff --git a/compton.h b/compton.h index 24325ab12..b3695af29 100644 --- a/compton.h +++ b/compton.h @@ -231,6 +231,9 @@ free_win_res(session_t *ps, win *w) { free(w->class_instance); free(w->class_general); free(w->role); +#ifdef CONFIG_VSYNC_OPENGL_GLSL + free_glx_bc(ps, &w->glx_blur_cache); +#endif } /** @@ -594,6 +597,24 @@ set_tgt_clip(session_t *ps, XserverRegion reg, const reg_data_t *pcache_reg) { } } +static bool +xr_blur_dst(session_t *ps, Picture tgt_buffer, + int x, int y, int wid, int hei, XFixed **blur_kerns, + XserverRegion reg_clip); + +/** + * Normalize a convolution kernel. + */ +static inline void +normalize_conv_kern(int wid, int hei, XFixed *kern) { + double sum = 0.0; + for (int i = 0; i < wid * hei; ++i) + sum += XFixedToDouble(kern[i]); + double factor = 1.0 / sum; + for (int i = 0; i < wid * hei; ++i) + kern[i] = XDoubleToFixed(XFixedToDouble(kern[i]) * factor); +} + static void paint_all(session_t *ps, XserverRegion region, XserverRegion region_real, win *t); @@ -1095,6 +1116,12 @@ vsync_opengl_wait(session_t *ps); static int vsync_opengl_oml_wait(session_t *ps); + +static void +vsync_opengl_swc_deinit(session_t *ps); + +static void +vsync_opengl_mswc_deinit(session_t *ps); #endif static void diff --git a/dbus.c b/dbus.c index e48aa1a02..f2b019aa3 100644 --- a/dbus.c +++ b/dbus.c @@ -717,6 +717,7 @@ cdbus_process_win_get(session_t *ps, DBusMessage *msg) { cdbus_m_win_get_do(wmwin, cdbus_reply_bool); cdbus_m_win_get_do(leader, cdbus_reply_wid); cdbus_m_win_get_do(focused_real, cdbus_reply_bool); + cdbus_m_win_get_do(fade_force, cdbus_reply_enum); cdbus_m_win_get_do(shadow_force, cdbus_reply_enum); cdbus_m_win_get_do(focused_force, cdbus_reply_enum); cdbus_m_win_get_do(invert_color_force, cdbus_reply_enum); @@ -770,8 +771,6 @@ cdbus_process_win_set(session_t *ps, DBusMessage *msg) { return true; } - ps->ev_received = true; - #define cdbus_m_win_set_do(tgt, type, real_type) \ if (!strcmp(MSTR(tgt), target)) { \ real_type val; \ @@ -789,6 +788,14 @@ cdbus_process_win_set(session_t *ps, DBusMessage *msg) { goto cdbus_process_win_set_success; } + if (!strcmp("fade_force", target)) { + cdbus_enum_t val = UNSET; + if (!cdbus_msg_get_arg(msg, 2, CDBUS_TYPE_ENUM, &val)) + return false; + win_set_fade_force(ps, w, val); + goto cdbus_process_win_set_success; + } + if (!strcmp("focused_force", target)) { cdbus_enum_t val = UNSET; if (!cdbus_msg_get_arg(msg, 2, CDBUS_TYPE_ENUM, &val)) @@ -912,6 +919,11 @@ cdbus_process_opts_get(session_t *ps, DBusMessage *msg) { cdbus_m_opts_get_do(shadow_opacity, cdbus_reply_double); cdbus_m_opts_get_do(clear_shadow, cdbus_reply_bool); + cdbus_m_opts_get_do(fade_delta, cdbus_reply_int32); + cdbus_m_opts_get_do(fade_in_step, cdbus_reply_int32); + cdbus_m_opts_get_do(fade_out_step, cdbus_reply_int32); + cdbus_m_opts_get_do(no_fading_openclose, cdbus_reply_bool); + cdbus_m_opts_get_do(blur_background, cdbus_reply_bool); cdbus_m_opts_get_do(blur_background_frame, cdbus_reply_bool); cdbus_m_opts_get_do(blur_background_fixed, cdbus_reply_bool); @@ -961,6 +973,42 @@ cdbus_process_opts_set(session_t *ps, DBusMessage *msg) { goto cdbus_process_opts_set_success; \ } + // fade_delta + if (!strcmp("fade_delta", target)) { + int32_t val = 0.0; + if (!cdbus_msg_get_arg(msg, 1, DBUS_TYPE_INT32, &val)) + return false; + ps->o.fade_delta = max_i(val, 1); + goto cdbus_process_opts_set_success; + } + + // fade_in_step + if (!strcmp("fade_in_step", target)) { + double val = 0.0; + if (!cdbus_msg_get_arg(msg, 1, DBUS_TYPE_DOUBLE, &val)) + return false; + ps->o.fade_in_step = normalize_d(val) * OPAQUE; + goto cdbus_process_opts_set_success; + } + + // fade_out_step + if (!strcmp("fade_out_step", target)) { + double val = 0.0; + if (!cdbus_msg_get_arg(msg, 1, DBUS_TYPE_DOUBLE, &val)) + return false; + ps->o.fade_out_step = normalize_d(val) * OPAQUE; + goto cdbus_process_opts_set_success; + } + + // no_fading_openclose + if (!strcmp("no_fading_openclose", target)) { + dbus_bool_t val = FALSE; + if (!cdbus_msg_get_arg(msg, 1, DBUS_TYPE_BOOLEAN, &val)) + return false; + opts_set_no_fading_openclose(ps, val); + goto cdbus_process_opts_set_success; + } + // unredir_if_possible if (!strcmp("unredir_if_possible", target)) { dbus_bool_t val = FALSE; @@ -1002,6 +1050,7 @@ cdbus_process_opts_set(session_t *ps, DBusMessage *msg) { const char * val = NULL; if (!cdbus_msg_get_arg(msg, 1, DBUS_TYPE_STRING, &val)) return false; + vsync_deinit(ps); if (!parse_vsync(ps, val)) { printf_errf("(): " CDBUS_ERROR_BADARG_S, 1, "Value invalid."); cdbus_reply_err(ps, msg, CDBUS_ERROR_BADARG, CDBUS_ERROR_BADARG_S, 1, "Value invalid."); diff --git a/opengl.c b/opengl.c index edd615132..635cd33ef 100644 --- a/opengl.c +++ b/opengl.c @@ -160,10 +160,13 @@ 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); + for (int i = 0; i < MAX_BLUR_PASS; ++i) { + glx_blur_pass_t *ppass = &ps->glx_blur_passes[i]; + if (ppass->frag_shader) + glDeleteShader(ppass->frag_shader); + if (ppass->prog) + glDeleteProgram(ppass->prog); + } #endif // Free FBConfigs @@ -199,8 +202,28 @@ glx_on_root_change(session_t *ps) { */ bool glx_init_blur(session_t *ps) { + assert(ps->o.blur_kerns[0]); + + // Allocate PBO if more than one blur kernel is present + if (ps->o.blur_kerns[1]) { +#ifdef CONFIG_VSYNC_OPENGL_FBO + // Try to generate a framebuffer + GLuint fbo = 0; + glGenFramebuffers(1, &fbo); + if (!fbo) { + printf_errf("(): Failed to generate Framebuffer. Cannot do " + "multi-pass blur with GLX backend."); + return false; + } + glDeleteFramebuffers(1, &fbo); +#else + printf_errf("(): FBO support not compiled in. Cannot do multi-pass blur " + "with GLX backend."); + return false; +#endif + } + #ifdef CONFIG_VSYNC_OPENGL_GLSL - // Build shader { static const char *FRAG_SHADER_BLUR_PREFIX = "#version 110\n" @@ -235,73 +258,81 @@ glx_init_blur(session_t *ps) { shader_add = FRAG_SHADER_BLUR_ADD_GPUSHADER4; } - int wid = XFixedToDouble(ps->o.blur_kern[0]), - hei = XFixedToDouble(ps->o.blur_kern[1]); - int nele = wid * hei - 1; - int len = strlen(FRAG_SHADER_BLUR_PREFIX) + strlen(sampler_type) + strlen(extension) + (strlen(shader_add) + strlen(texture_func) + 42) * nele + strlen(FRAG_SHADER_BLUR_SUFFIX) + strlen(texture_func) + 12 + 1; - char *shader_str = calloc(len, sizeof(char)); - if (!shader_str) { - printf_errf("(): Failed to allocate %d bytes for shader string.", len); - return false; - } - { - char *pc = shader_str; - sprintf(pc, FRAG_SHADER_BLUR_PREFIX, extension, sampler_type); - pc += strlen(pc); - assert(strlen(shader_str) < len); - - double sum = 0.0; - for (int i = 0; i < hei; ++i) { - for (int j = 0; j < wid; ++j) { - if (hei / 2 == i && wid / 2 == j) - continue; - double val = XFixedToDouble(ps->o.blur_kern[2 + i * wid + j]); - if (0.0 == val) - continue; - sum += val; - sprintf(pc, shader_add, val, texture_func, j - wid / 2, i - hei / 2); + for (int i = 0; i < MAX_BLUR_PASS && ps->o.blur_kerns[i]; ++i) { + XFixed *kern = ps->o.blur_kerns[i]; + if (!kern) + break; + + glx_blur_pass_t *ppass = &ps->glx_blur_passes[i]; + + // Build shader + { + int wid = XFixedToDouble(kern[0]), hei = XFixedToDouble(kern[1]); + int nele = wid * hei - 1; + int len = strlen(FRAG_SHADER_BLUR_PREFIX) + strlen(sampler_type) + strlen(extension) + (strlen(shader_add) + strlen(texture_func) + 42) * nele + strlen(FRAG_SHADER_BLUR_SUFFIX) + strlen(texture_func) + 12 + 1; + char *shader_str = calloc(len, sizeof(char)); + if (!shader_str) { + printf_errf("(): Failed to allocate %d bytes for shader string.", len); + return false; + } + { + char *pc = shader_str; + sprintf(pc, FRAG_SHADER_BLUR_PREFIX, extension, sampler_type); pc += strlen(pc); assert(strlen(shader_str) < len); + + double sum = 0.0; + for (int j = 0; j < hei; ++j) { + for (int k = 0; k < wid; ++k) { + if (hei / 2 == j && wid / 2 == k) + continue; + double val = XFixedToDouble(kern[2 + j * wid + k]); + if (0.0 == val) + continue; + sum += val; + sprintf(pc, shader_add, val, texture_func, k - wid / 2, j - hei / 2); + pc += strlen(pc); + assert(strlen(shader_str) < len); + } + } + + sprintf(pc, FRAG_SHADER_BLUR_SUFFIX, texture_func, sum); + assert(strlen(shader_str) < len); } + ppass->frag_shader = glx_create_shader(GL_FRAGMENT_SHADER, shader_str); + free(shader_str); } - sprintf(pc, FRAG_SHADER_BLUR_SUFFIX, texture_func, sum); - assert(strlen(shader_str) < len); -#ifdef DEBUG_GLX_GLSL - fputs(shader_str, stdout); - fflush(stdout); -#endif - } - ps->glx_frag_shader_blur = glx_create_shader(GL_FRAGMENT_SHADER, shader_str); - free(extension); - free(shader_str); - } - - if (!ps->glx_frag_shader_blur) { - printf_errf("(): Failed to create fragment shader."); - return false; - } + if (!ppass->frag_shader) { + printf_errf("(): Failed to create fragment shader %d.", i); + 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; - } + // Build program + ppass->prog = glx_create_program(&ppass->frag_shader, 1); + if (!ppass->prog) { + printf_errf("(): Failed to create GLSL program."); + return false; + } + // Get uniform addresses #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 "'. Might be troublesome."); \ - } \ -} + ppass->target = glGetUniformLocation(ppass->prog, name); \ + if (ppass->target < 0) { \ + printf_errf("(): Failed to get location of %d-th uniform '" name "'. Might be troublesome.", i); \ + } \ + } - P_GET_UNIFM_LOC("factor_center", glx_prog_blur_unifm_factor_center); - if (!ps->o.glx_use_gpushader4) { - P_GET_UNIFM_LOC("offset_x", glx_prog_blur_unifm_offset_x); - P_GET_UNIFM_LOC("offset_y", glx_prog_blur_unifm_offset_y); + P_GET_UNIFM_LOC("factor_center", unifm_factor_center); + if (!ps->o.glx_use_gpushader4) { + P_GET_UNIFM_LOC("offset_x", unifm_offset_x); + P_GET_UNIFM_LOC("offset_y", unifm_offset_y); + } +#undef P_GET_UNIFM_LOC + } + free(extension); } -#undef P_GET_UNIFM_LOC #ifdef DEBUG_GLX_ERR glx_check_err(ps); @@ -841,9 +872,9 @@ glx_set_clip(session_t *ps, XserverRegion reg, const reg_data_t *pcache_reg) { } \ glBegin(GL_QUADS); \ \ - for (int i = 0; i < nrects; ++i) { \ + for (int ri = 0; ri < nrects; ++ri) { \ XRectangle crect; \ - rect_crop(&crect, &rects[i], &rec_all); \ + rect_crop(&crect, &rects[ri], &rec_all); \ \ if (!crect.width || !crect.height) \ continue; \ @@ -856,21 +887,69 @@ glx_set_clip(session_t *ps, XserverRegion reg, const reg_data_t *pcache_reg) { cxfree(rects); \ free_region(ps, ®_new); \ +static inline GLuint +glx_gen_texture(session_t *ps, GLenum tex_tgt, int width, int height) { + GLuint tex = 0; + glGenTextures(1, &tex); + if (!tex) return 0; + glEnable(tex_tgt); + glBindTexture(tex_tgt, tex); + glTexParameteri(tex_tgt, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(tex_tgt, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(tex_tgt, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(tex_tgt, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexImage2D(tex_tgt, 0, GL_RGB, width, height, 0, GL_RGB, + GL_UNSIGNED_BYTE, NULL); + glBindTexture(tex_tgt, 0); + + return tex; +} + +static inline void +glx_copy_region_to_tex(session_t *ps, GLenum tex_tgt, int basex, int basey, + int dx, int dy, int width, int height) { + if (width > 0 && height > 0) + glCopyTexSubImage2D(tex_tgt, 0, dx - basex, dy - basey, + dx, ps->root_height - dy - height, width, height); +} + +#ifdef CONFIG_VSYNC_OPENGL_GLSL +/** + * Blur contents in a particular region. + */ bool glx_blur_dst(session_t *ps, int dx, int dy, int width, int height, float z, - GLfloat factor_center, XserverRegion reg_tgt, const reg_data_t *pcache_reg) { - // Read destination pixels into a texture - GLuint tex_scr = 0; - glGenTextures(1, &tex_scr); - if (!tex_scr) { - printf_errf("(): Failed to allocate texture."); - return false; - } + GLfloat factor_center, + XserverRegion reg_tgt, const reg_data_t *pcache_reg, + glx_blur_cache_t *pbc) { + assert(ps->glx_blur_passes[0].prog); + const bool more_passes = ps->glx_blur_passes[1].prog; + const bool have_scissors = glIsEnabled(GL_SCISSOR_TEST); + const bool have_stencil = glIsEnabled(GL_STENCIL_TEST); + bool ret = false; + + // Calculate copy region size + glx_blur_cache_t ibc = { .width = 0, .height = 0 }; + if (!pbc) + pbc = &ibc; int mdx = dx, mdy = dy, mwidth = width, mheight = height; +#ifdef DEBUG_GLX + printf_dbgf("(): %d, %d, %d, %d\n", mdx, mdy, mwidth, mheight); +#endif + + /* if (ps->o.resize_damage > 0) { - int inc_x = min_i(ps->o.resize_damage, XFixedToDouble(ps->o.blur_kern[0]) / 2), - inc_y = min_i(ps->o.resize_damage, XFixedToDouble(ps->o.blur_kern[1]) / 2); + int inc_x = 0, inc_y = 0; + for (int i = 0; i < MAX_BLUR_PASS; ++i) { + XFixed *kern = ps->o.blur_kerns[i]; + if (!kern) break; + inc_x += XFixedToDouble(kern[0]) / 2; + inc_y += XFixedToDouble(kern[1]) / 2; + } + inc_x = min_i(ps->o.resize_damage, inc_x); + inc_y = min_i(ps->o.resize_damage, inc_y); + mdx = max_i(dx - inc_x, 0); mdy = max_i(dy - inc_y, 0); int mdx2 = min_i(dx + width + inc_x, ps->root_width), @@ -878,24 +957,57 @@ glx_blur_dst(session_t *ps, int dx, int dy, int width, int height, float z, mwidth = mdx2 - mdx; mheight = mdy2 - mdy; } + */ GLenum tex_tgt = GL_TEXTURE_RECTANGLE; if (ps->glx_has_texture_non_power_of_two) tex_tgt = GL_TEXTURE_2D; - glEnable(tex_tgt); - glBindTexture(tex_tgt, tex_scr); - glTexParameteri(tex_tgt, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(tex_tgt, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(tex_tgt, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(tex_tgt, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glTexImage2D(tex_tgt, 0, GL_RGB, mwidth, mheight, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL); - glCopyTexSubImage2D(tex_tgt, 0, 0, 0, mdx, ps->root_height - mdy - mheight, mwidth, mheight); + // Free textures if size inconsistency discovered + if (mwidth != pbc->width || mheight != pbc->height) + free_glx_bc_resize(ps, pbc); + + // Generate FBO and textures if needed + if (!pbc->textures[0]) + pbc->textures[0] = glx_gen_texture(ps, tex_tgt, mwidth, mheight); + GLuint tex_scr = pbc->textures[0]; + if (more_passes && !pbc->textures[1]) + pbc->textures[1] = glx_gen_texture(ps, tex_tgt, mwidth, mheight); + pbc->width = mwidth; + pbc->height = mheight; + GLuint tex_scr2 = pbc->textures[1]; +#ifdef CONFIG_VSYNC_OPENGL_FBO + if (more_passes && !pbc->fbo) + glGenFramebuffers(1, &pbc->fbo); + const GLuint fbo = pbc->fbo; +#endif -#ifdef DEBUG_GLX - printf_dbgf("(): %d, %d, %d, %d\n", mdx, ps->root_height - mdy - mheight, mwidth, mheight); + if (!tex_scr || (more_passes && !tex_scr2)) { + printf_errf("(): Failed to allocate texture."); + goto glx_blur_dst_end; + } +#ifdef CONFIG_VSYNC_OPENGL_FBO + if (more_passes && !fbo) { + printf_errf("(): Failed to allocate framebuffer."); + goto glx_blur_dst_end; + } #endif + // Read destination pixels into a texture + glEnable(tex_tgt); + glBindTexture(tex_tgt, tex_scr); + glx_copy_region_to_tex(ps, tex_tgt, mdx, mdy, mdx, mdy, mwidth, mheight); + /* + if (tex_scr2) { + glBindTexture(tex_tgt, tex_scr2); + glx_copy_region_to_tex(ps, tex_tgt, mdx, mdy, mdx, mdy, mwidth, dx - mdx); + glx_copy_region_to_tex(ps, tex_tgt, mdx, mdy, mdx, dy + height, + mwidth, mdy + mheight - dy - height); + glx_copy_region_to_tex(ps, tex_tgt, mdx, mdy, mdx, dy, dx - mdx, height); + glx_copy_region_to_tex(ps, tex_tgt, mdx, mdy, dx + width, dy, + mdx + mwidth - dx - width, height); + } */ + // Texture scaling factor GLfloat texfac_x = 1.0f, texfac_y = 1.0f; if (GL_TEXTURE_2D == tex_tgt) { @@ -904,67 +1016,130 @@ glx_blur_dst(session_t *ps, int dx, int dy, int width, int height, float z, } // Paint it back - // 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); + if (more_passes) { + glDisable(GL_STENCIL_TEST); + glDisable(GL_SCISSOR_TEST); + } - glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); -#ifdef CONFIG_VSYNC_OPENGL_GLSL - glUseProgram(ps->glx_prog_blur); - if (ps->glx_prog_blur_unifm_offset_x >= 0) - glUniform1f(ps->glx_prog_blur_unifm_offset_x, texfac_x); - if (ps->glx_prog_blur_unifm_offset_y >= 0) - glUniform1f(ps->glx_prog_blur_unifm_offset_y, texfac_y); - if (ps->glx_prog_blur_unifm_factor_center >= 0) - glUniform1f(ps->glx_prog_blur_unifm_factor_center, factor_center); + bool last_pass = false; + for (int i = 0; !last_pass; ++i) { + last_pass = !ps->glx_blur_passes[i + 1].prog; + assert(i < MAX_BLUR_PASS - 1); + const glx_blur_pass_t *ppass = &ps->glx_blur_passes[i]; + assert(ppass->prog); + + assert(tex_scr); + glBindTexture(tex_tgt, tex_scr); + +#ifdef CONFIG_VSYNC_OPENGL_FBO + if (!last_pass) { + static const GLenum DRAWBUFS[2] = { GL_COLOR_ATTACHMENT0 }; + glBindFramebuffer(GL_FRAMEBUFFER, fbo); + glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, tex_scr2, 0); + glDrawBuffers(1, DRAWBUFS); + if (glCheckFramebufferStatus(GL_FRAMEBUFFER) + != GL_FRAMEBUFFER_COMPLETE) { + printf_errf("(): Framebuffer attachment failed."); + goto glx_blur_dst_end; + } + } + else { + static const GLenum DRAWBUFS[2] = { GL_BACK }; + glBindFramebuffer(GL_FRAMEBUFFER, 0); + glDrawBuffers(1, DRAWBUFS); + if (have_scissors) + glEnable(GL_SCISSOR_TEST); + if (have_stencil) + glEnable(GL_STENCIL_TEST); + } #endif - { - P_PAINTREG_START(); + // 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); + glUseProgram(ppass->prog); + if (ppass->unifm_offset_x >= 0) + glUniform1f(ppass->unifm_offset_x, texfac_x); + if (ppass->unifm_offset_y >= 0) + glUniform1f(ppass->unifm_offset_y, texfac_y); + if (ppass->unifm_factor_center >= 0) + glUniform1f(ppass->unifm_factor_center, factor_center); + { - const GLfloat rx = (crect.x - mdx) * texfac_x; - const GLfloat ry = (mheight - (crect.y - mdy)) * texfac_y; - const GLfloat rxe = rx + crect.width * texfac_x; - const GLfloat rye = ry - crect.height * texfac_y; - const GLfloat rdx = crect.x; - const GLfloat rdy = ps->root_height - crect.y; - const GLfloat rdxe = rdx + crect.width; - const GLfloat rdye = rdy - crect.height; + P_PAINTREG_START(); + { + const GLfloat rx = (crect.x - mdx) * texfac_x; + const GLfloat ry = (mheight - (crect.y - mdy)) * texfac_y; + const GLfloat rxe = rx + crect.width * texfac_x; + const GLfloat rye = ry - crect.height * texfac_y; + GLfloat rdx = crect.x - mdx; + GLfloat rdy = mheight - crect.y + mdy; + GLfloat rdxe = rdx + crect.width; + GLfloat rdye = rdy - crect.height; + + if (last_pass) { + rdx = crect.x; + rdy = ps->root_height - crect.y; + rdxe = rdx + crect.width; + rdye = rdy - crect.height; + } #ifdef DEBUG_GLX - printf_dbgf("(): %f, %f, %f, %f -> %d, %d, %d, %d\n", rx, ry, rxe, rye, rdx, rdy, rdxe, rdye); + printf_dbgf("(): %f, %f, %f, %f -> %f, %f, %f, %f\n", rx, ry, rxe, rye, rdx, rdy, rdxe, rdye); #endif - glTexCoord2f(rx, ry); - glVertex3f(rdx, rdy, z); + glTexCoord2f(rx, ry); + glVertex3f(rdx, rdy, z); - glTexCoord2f(rxe, ry); - glVertex3f(rdxe, rdy, z); + glTexCoord2f(rxe, ry); + glVertex3f(rdxe, rdy, z); - glTexCoord2f(rxe, rye); - glVertex3f(rdxe, rdye, z); + glTexCoord2f(rxe, rye); + glVertex3f(rdxe, rdye, z); - glTexCoord2f(rx, rye); - glVertex3f(rdx, rdye, z); + glTexCoord2f(rx, rye); + glVertex3f(rdx, rdye, z); + } + P_PAINTREG_END(); + } + + glUseProgram(0); + + // Swap tex_scr and tex_scr2 + { + GLuint tmp = tex_scr2; + tex_scr2 = tex_scr; + tex_scr = tmp; } - P_PAINTREG_END(); } -#ifdef CONFIG_VSYNC_OPENGL_GLSL - glUseProgram(0); + ret = true; + +glx_blur_dst_end: +#ifdef CONFIG_VSYNC_OPENGL_FBO + glBindFramebuffer(GL_FRAMEBUFFER, 0); #endif - glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); glBindTexture(tex_tgt, 0); - glDeleteTextures(1, &tex_scr); glDisable(tex_tgt); + if (have_scissors) + glEnable(GL_SCISSOR_TEST); + if (have_stencil) + glEnable(GL_STENCIL_TEST); + + if (&ibc == pbc) { + free_glx_bc(ps, pbc); + } #ifdef DEBUG_GLX_ERR glx_check_err(ps); #endif - return true; + return ret; } +#endif bool glx_dim_dst(session_t *ps, int dx, int dy, int width, int height, float z, @@ -1156,7 +1331,7 @@ glx_render(session_t *ps, const glx_texture_t *ptex, } #ifdef DEBUG_GLX - printf_dbgf("(): Rect %d: %f, %f, %f, %f -> %d, %d, %d, %d\n", i, rx, ry, rxe, rye, rdx, rdy, rdxe, rdye); + printf_dbgf("(): Rect %d: %f, %f, %f, %f -> %d, %d, %d, %d\n", ri, rx, ry, rxe, rye, rdx, rdy, rdxe, rdye); #endif #define P_TEXCOORD(cx, cy) { \ @@ -1318,6 +1493,11 @@ glx_swap_copysubbuffermesa(session_t *ps, XserverRegion reg) { #ifdef CONFIG_VSYNC_OPENGL_GLSL GLuint glx_create_shader(GLenum shader_type, const char *shader_str) { +#ifdef DEBUG_GLX_GLSL + printf("glx_create_shader(): ===\n%s\n===\n", shader_str); + fflush(stdout); +#endif + bool success = false; GLuint shader = glCreateShader(shader_type); if (!shader) {