From a7c5c43d3229fab3bd18dabae691836edd59ad9b Mon Sep 17 00:00:00 2001 From: Bernd Busse Date: Tue, 20 Feb 2018 00:47:25 +0100 Subject: [PATCH 1/8] Add multi-pass kawase blur for GLX backend --- src/common.h | 127 +++++++++++++- src/compton.c | 44 ++++- src/opengl.c | 461 +++++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 620 insertions(+), 12 deletions(-) diff --git a/src/common.h b/src/common.h index 2b75ff7a..e1571b08 100644 --- a/src/common.h +++ b/src/common.h @@ -352,6 +352,13 @@ typedef enum { NUM_VSYNC, } vsync_t; +/// Possible background blur algorithms. +enum blur_method { + BLRMTHD_CONV, + BLRMTHD_KAWASE, + NUM_BLRMTHD, +}; + /// @brief Possible backends of compton. enum backend { BKEND_XRENDER, @@ -473,13 +480,18 @@ typedef struct { GLint unifm_offset_y; /// Location of uniform "factor_center" in blur GLSL program. GLint unifm_factor_center; + /// Location of uniform "offset" in blur GLSL program. + GLint unifm_offset; + /// Location of uniform "halfpixel" in blur GLSL program. + GLint unifm_halfpixel; } glx_blur_pass_t; typedef struct { /// Framebuffer used for blurring. GLuint fbo; + // FIXME.kawase: Set maximum number of textures for kawase blur /// Textures used for blurring. - GLuint textures[2]; + GLuint textures[6]; // 4 + 2 /// Width of the textures. int width; /// Height of the textures. @@ -707,8 +719,13 @@ typedef struct _options_t { bool blur_background_fixed; /// Background blur blacklist. A linked list of conditions. c2_lptr_t *blur_background_blacklist; + /// Blur method. + enum blur_method blur_method; /// Blur convolution kernel. XFixed *blur_kerns[MAX_BLUR_PASS]; + /// Blur strength. + int blur_strength_iterations; + float blur_strength_offset; /// 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 @@ -1263,6 +1280,7 @@ typedef enum { extern const char * const WINTYPES[NUM_WINTYPES]; extern const char * const VSYNC_STRS[NUM_VSYNC + 1]; extern const char * const BACKEND_STRS[NUM_BKEND + 1]; +extern const char * const BLUR_METHOD_STRS[NUM_BLRMTHD + 1]; extern session_t *ps_g; // == Debugging code == @@ -1668,6 +1686,108 @@ parse_vsync(session_t *ps, const char *str) { return false; } +/** + * Parse a blur_method option argument. + */ +static inline bool +parse_blur_method(session_t *ps, const char *str) { + for (enum blur_method i = 0; BLUR_METHOD_STRS[i]; ++i) + if (!strcasecmp(str, BLUR_METHOD_STRS[i])) { + ps->o.blur_method = i; + return true; + } + + printf_errf("(\"%s\"): Invalid blur_method argument.", str); + return false; +} + +/** + * Parse a blur_strength option argument. + */ +static inline bool +parse_blur_strength(session_t *ps, const char *str) { + const char *endptr = NULL; + long level = strtol(str, (char **) &endptr, 0); + if (!endptr || endptr == str) { + printf_errf("(\"%s\"): Invalid number.", str); + return false; + } + while (isspace(*endptr)) + ++endptr; + if (*endptr) { + printf_errf("(\"%s\"): Trailing characters.", str); + return false; + } + + switch (level) { + case 1: + ps->o.blur_strength_iterations = 1; + ps->o.blur_strength_offset = 1.5; + break; + case 2: + ps->o.blur_strength_iterations = 1; + ps->o.blur_strength_offset = 2.0; + break; + case 3: + ps->o.blur_strength_iterations = 2; + ps->o.blur_strength_offset = 2.5; + break; + case 4: + ps->o.blur_strength_iterations = 2; + ps->o.blur_strength_offset = 3.0; + break; + case 5: + ps->o.blur_strength_iterations = 3; + ps->o.blur_strength_offset = 2.6; + break; + case 6: + ps->o.blur_strength_iterations = 3; + ps->o.blur_strength_offset = 3.2; + break; + case 7: + ps->o.blur_strength_iterations = 3; + ps->o.blur_strength_offset = 3.8; + break; + case 8: + ps->o.blur_strength_iterations = 3; + ps->o.blur_strength_offset = 4.4; + break; + case 9: + ps->o.blur_strength_iterations = 3; + ps->o.blur_strength_offset = 5.0; + break; + case 10: + ps->o.blur_strength_iterations = 4; + ps->o.blur_strength_offset = 3.833; + break; + case 11: + ps->o.blur_strength_iterations = 4; + ps->o.blur_strength_offset = 4.667; + break; + case 12: + ps->o.blur_strength_iterations = 4; + ps->o.blur_strength_offset = 5.5; + break; + case 13: + ps->o.blur_strength_iterations = 4; + ps->o.blur_strength_offset = 6.333; + break; + case 14: + ps->o.blur_strength_iterations = 4; + ps->o.blur_strength_offset = 7.167; + break; + case 15: + ps->o.blur_strength_iterations = 4; + ps->o.blur_strength_offset = 8.0; + break; + default: + printf_errf("(\"%s\"): Invalid blur_strength argument. Needs to be a number between 1 and 15.", str); + return false; + } + + return true; +} + /** * Parse a backend option argument. */ @@ -2276,8 +2396,9 @@ free_glx_fbo(session_t *ps, GLuint *pfbo) { */ 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]); + // FIXME.kawase: Use maximum number of textures + for (int i = 0; i < 6; i++) + free_texture_r(ps, &pbc->textures[i]); pbc->width = 0; pbc->height = 0; } diff --git a/src/compton.c b/src/compton.c index 92935a3d..7c847911 100644 --- a/src/compton.c +++ b/src/compton.c @@ -43,6 +43,13 @@ const char * const VSYNC_STRS[NUM_VSYNC + 1] = { NULL }; +/// Names of blur_methods. +const char * const BLUR_METHOD_STRS[NUM_BLRMTHD + 1] = { + "convolution", // BLRMTHD_CONV + "kawase", // BLRMTHD_KAWASE + NULL +}; + /// Names of backends. const char * const BACKEND_STRS[NUM_BKEND + 1] = { "xrender", // BKEND_XRENDER @@ -4699,7 +4706,13 @@ usage(int ret) { " Use fixed blur strength instead of adjusting according to window\n" " opacity.\n" "\n" + // FIXME.kawse: kawase algorithm only available when compiled with GLX support + "--blur-method algorithm\n" + " Specify the algorithm for background blur. It is either one of:\n" + " convolution (default), kawase\n" + "\n" "--blur-kern matrix\n" + " Only valid for '--blur-method convolution'!\n" " Specify the blur convolution kernel, with the following format:\n" " WIDTH,HEIGHT,ELE1,ELE2,ELE3,ELE4,ELE5...\n" " The element in the center must not be included, it will be forever\n" @@ -4713,6 +4726,11 @@ usage(int ret) { " 7x7box, 3x3gaussian, 5x5gaussian, 7x7gaussian, 9x9gaussian,\n" " 11x11gaussian.\n" "\n" + // FIXME.kawse: kawase algorithm only available when compiled with GLX support + "--blur-strength integer\n" + " Only valid for '--blur-method kawase'!\n" + " The strength of the kawase blur as an integer between 1 and 15.\n" + "\n" "--blur-background-exclude condition\n" " Exclude conditions for background blur.\n" "\n" @@ -5618,10 +5636,19 @@ parse_config(session_t *ps, struct options_tmp *pcfgtmp) { // --blur-background-fixed lcfg_lookup_bool(&cfg, "blur-background-fixed", &ps->o.blur_background_fixed); + // --blur-method + if (config_lookup_string(&cfg, "blur-method", &sval) + && !parse_blur_method(ps, sval)) + exit(1); // --blur-kern if (config_lookup_string(&cfg, "blur-kern", &sval) && !parse_conv_kern_lst(ps, sval, ps->o.blur_kerns, MAX_BLUR_PASS)) exit(1); + // --blur-strength + //lcfg_lookup_int(&cfg, "blur-strength", &ps->o.blur_strength); + if (config_lookup_string(&cfg, "blur-strength", &sval) + && !parse_blur_strength(ps, sval)) + exit(1); // --resize-damage lcfg_lookup_int(&cfg, "resize-damage", &ps->o.resize_damage); // --glx-no-stencil @@ -5756,6 +5783,8 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { { "version", no_argument, NULL, 318 }, { "no-x-selection", no_argument, NULL, 319 }, { "no-name-pixmap", no_argument, NULL, 320 }, + { "blur-method", required_argument, NULL, 321 }, + { "blur-strength", required_argument, NULL, 322 }, { "reredir-on-root-change", no_argument, NULL, 731 }, { "glx-reinit-on-root-change", no_argument, NULL, 732 }, // Must terminate with a NULL entry @@ -6027,6 +6056,16 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { ps->o.glx_fshader_win_str = mstrcpy(optarg); break; P_CASEBOOL(319, no_x_selection); + case 321: + // --blur-method + if (!parse_blur_method(ps, optarg)) + exit(1); + break; + case 322: + // --blur-strength + if (!parse_blur_strength(ps, optarg)) + exit(1); + break; P_CASEBOOL(731, reredir_on_root_change); P_CASEBOOL(732, glx_reinit_on_root_change); default: @@ -6092,7 +6131,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_kerns[0]) { + if (ps->o.blur_background && (BLRMTHD_CONV == ps->o.blur_method) && !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 @@ -7031,7 +7070,10 @@ session_init(session_t *ps_old, int argc, char **argv) { .blur_background_frame = false, .blur_background_fixed = false, .blur_background_blacklist = NULL, + .blur_method = BLRMTHD_CONV, .blur_kerns = { NULL }, + .blur_strength_iterations = 1, + .blur_strength_offset = 1.5, .inactive_dim = 0.0, .inactive_dim_fixed = false, .invert_color_list = NULL, diff --git a/src/opengl.c b/src/opengl.c index 5a98f4e0..120448af 100644 --- a/src/opengl.c +++ b/src/opengl.c @@ -378,7 +378,7 @@ glx_on_root_change(session_t *ps) { * Initialize GLX blur filter. */ bool -glx_init_blur(session_t *ps) { +glx_init_conv_blur(session_t *ps) { assert(ps->o.blur_kerns[0]); // Allocate PBO if more than one blur kernel is present @@ -400,7 +400,6 @@ glx_init_blur(session_t *ps) { #endif } -#ifdef CONFIG_VSYNC_OPENGL_GLSL { char *lc_numeric_old = mstrcpy(setlocale(LC_NUMERIC, NULL)); // Enforce LC_NUMERIC locale "C" here to make sure decimal point is sane @@ -481,6 +480,9 @@ glx_init_blur(session_t *ps) { sprintf(pc, FRAG_SHADER_BLUR_SUFFIX, texture_func, sum); assert(strlen(shader_str) < len); } +#ifdef DEBUG_GLX + printf_dbgf("(): Generated convolution shader:\n%s\n", shader_str); +#endif ppass->frag_shader = glx_create_shader(GL_FRAGMENT_SHADER, shader_str); free(shader_str); } @@ -524,6 +526,195 @@ glx_init_blur(session_t *ps) { glx_check_err(ps); return true; +} + +bool +glx_init_kawase_blur(session_t *ps) { + // Allocate PBO to check for multi-pass support +#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 + } + + { + char *lc_numeric_old = mstrcpy(setlocale(LC_NUMERIC, NULL)); + // Enforce LC_NUMERIC locale "C" here to make sure decimal point is sane + // Thanks to hiciu for reporting. + setlocale(LC_NUMERIC, "C"); + + static const char *FRAG_SHADER_PREFIX = + "#version 110\n" + "%s" // extensions + "uniform float offset;\n" + "uniform vec2 halfpixel;\n" + "uniform %s tex_scr;\n" // sampler2D | sampler2DRect + "void main()\n" + "{\n" + " vec2 uv = gl_TexCoord[0].xy;\n" + " \n"; + + // Fragment shader (Dual Kawase Blur) - Downsample + static const char *FRAG_SHADER_KAWASE_DOWN = + " vec4 sum = %1$s(tex_scr, uv) * 4.0;\n" // texture | texture2D + " sum += %1$s(tex_scr, uv - halfpixel.xy * offset);\n" + " sum += %1$s(tex_scr, uv + halfpixel.xy * offset);\n" + " sum += %1$s(tex_scr, uv + vec2(halfpixel.x, -halfpixel.y) * offset);\n" + " sum += %1$s(tex_scr, uv - vec2(halfpixel.x, -halfpixel.y) * offset);\n" + " \n" + " gl_FragColor = sum / 8.0;\n" + "}\n"; + + // Fragment shader (Dual Kawase Blur) - Upsample + static const char *FRAG_SHADER_KAWASE_UP = + " vec4 sum = %1$s(tex_scr, uv + vec2(-halfpixel.x * 2.0, 0.0) * offset);\n" // texture | texture2D + " sum += %1$s(tex_scr, uv + vec2(-halfpixel.x, halfpixel.y) * offset) * 2.0;\n" + " sum += %1$s(tex_scr, uv + vec2(0.0, halfpixel.y * 2.0) * offset);\n" + " sum += %1$s(tex_scr, uv + vec2(halfpixel.x, halfpixel.y) * offset) * 2.0;\n" + " sum += %1$s(tex_scr, uv + vec2(halfpixel.x * 2.0, 0.0) * offset);\n" + " sum += %1$s(tex_scr, uv + vec2(halfpixel.x, -halfpixel.y) * offset) * 2.0;\n" + " sum += %1$s(tex_scr, uv + vec2(0.0, -halfpixel.y * 2.0) * offset);\n" + " sum += %1$s(tex_scr, uv + vec2(-halfpixel.x, -halfpixel.y) * offset) * 2.0;\n" + " \n" + " gl_FragColor = sum / 12.0;\n" + "}\n"; + + const bool use_texture_rect = !ps->psglx->has_texture_non_power_of_two; + const char *sampler_type = (use_texture_rect ? + "sampler2DRect": "sampler2D"); + const char *texture_func = (use_texture_rect ? + "texture2DRect": "texture2D"); + char *extension = mstrcpy(""); + if (use_texture_rect) + mstrextend(&extension, "#extension GL_ARB_texture_rectangle : require\n"); + + // Build kawase downsample shader + glx_blur_pass_t *down_pass = &ps->psglx->blur_passes[0]; + { + int len = strlen(FRAG_SHADER_PREFIX) + strlen(extension) + (strlen(FRAG_SHADER_KAWASE_DOWN) + strlen(texture_func)) * 5 + 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_PREFIX, extension, sampler_type); + pc += strlen(pc); + assert(strlen(shader_str) < len); + + sprintf(pc, FRAG_SHADER_KAWASE_DOWN, texture_func); + assert(strlen(shader_str) < len); +#ifdef DEBUG_GLX + printf_dbgf("(): Generated kawase downsample shader:\n%s\n", shader_str); +#endif + down_pass->frag_shader = glx_create_shader(GL_FRAGMENT_SHADER, shader_str); + free(shader_str); + + if (!down_pass->frag_shader) { + printf_errf("(): Failed to create kawase downsample fragment shader."); + return false; + } + + // Build program + down_pass->prog = glx_create_program(&down_pass->frag_shader, 1); + if (!down_pass->prog) { + printf_errf("(): Failed to create GLSL program."); + return false; + } + + // Get uniform addresses +#define P_GET_UNIFM_LOC(name, target) { \ + down_pass->target = glGetUniformLocation(down_pass->prog, name); \ + if (down_pass->target < 0) { \ + printf_errf("(): Failed to get location of kawase downsample uniform '" name "'. Might be troublesome."); \ + } \ + } + P_GET_UNIFM_LOC("offset", unifm_offset); + P_GET_UNIFM_LOC("halfpixel", unifm_halfpixel); +#undef P_GET_UNIFM_LOC + } + + // Build kawase downsample shader + glx_blur_pass_t *up_pass = &ps->psglx->blur_passes[1]; + { + int len = strlen(FRAG_SHADER_PREFIX) + strlen(extension) + (strlen(FRAG_SHADER_KAWASE_UP) + strlen(texture_func)) * 8 + 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_PREFIX, extension, sampler_type); + pc += strlen(pc); + assert(strlen(shader_str) < len); + + sprintf(pc, FRAG_SHADER_KAWASE_UP, texture_func); + assert(strlen(shader_str) < len); +#ifdef DEBUG_GLX + printf_dbgf("(): Generated kawase upsample shader:\n%s\n", shader_str); +#endif + up_pass->frag_shader = glx_create_shader(GL_FRAGMENT_SHADER, shader_str); + free(shader_str); + + if (!up_pass->frag_shader) { + printf_errf("(): Failed to create kawase upsample fragment shader."); + return false; + } + + // Build program + up_pass->prog = glx_create_program(&up_pass->frag_shader, 1); + if (!up_pass->prog) { + printf_errf("(): Failed to create GLSL program."); + return false; + } + + // Get uniform addresses +#define P_GET_UNIFM_LOC(name, target) { \ + up_pass->target = glGetUniformLocation(up_pass->prog, name); \ + if (up_pass->target < 0) { \ + printf_errf("(): Failed to get location of kawase upsample uniform '" name "'. Might be troublesome."); \ + } \ + } + P_GET_UNIFM_LOC("offset", unifm_offset); + P_GET_UNIFM_LOC("halfpixel", unifm_halfpixel); +#undef P_GET_UNIFM_LOC + } + + free(extension); + + // Restore LC_NUMERIC + setlocale(LC_NUMERIC, lc_numeric_old); + free(lc_numeric_old); + } + + glx_check_err(ps); + + return true; +} + +bool +glx_init_blur(session_t *ps) { +#ifdef CONFIG_VSYNC_OPENGL_GLSL + switch (ps->o.blur_method) { + case BLRMTHD_CONV: + return glx_init_conv_blur(ps); + case BLRMTHD_KAWASE: + return glx_init_kawase_blur(ps); + } #else printf_errf("(): GLSL support not compiled in. Cannot do blur with GLX backend."); return false; @@ -1162,11 +1353,10 @@ glx_copy_region_to_tex(session_t *ps, GLenum tex_tgt, int basex, int basey, * Blur contents in a particular region. */ bool -glx_blur_dst(session_t *ps, int dx, int dy, int width, int height, float z, +glx_conv_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, glx_blur_cache_t *pbc) { - assert(ps->psglx->blur_passes[0].prog); const bool more_passes = ps->psglx->blur_passes[1].prog; const bool have_scissors = glIsEnabled(GL_SCISSOR_TEST); const bool have_stencil = glIsEnabled(GL_STENCIL_TEST); @@ -1228,12 +1418,12 @@ glx_blur_dst(session_t *ps, int dx, int dy, int width, int height, float z, if (!tex_scr || (more_passes && !tex_scr2)) { printf_errf("(): Failed to allocate texture."); - goto glx_blur_dst_end; + goto glx_conv_blur_dst_end; } #ifdef CONFIG_VSYNC_OPENGL_FBO if (more_passes && !fbo) { printf_errf("(): Failed to allocate framebuffer."); - goto glx_blur_dst_end; + goto glx_conv_blur_dst_end; } #endif @@ -1285,7 +1475,7 @@ glx_blur_dst(session_t *ps, int dx, int dy, int width, int height, float z, if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { printf_errf("(): Framebuffer attachment failed."); - goto glx_blur_dst_end; + goto glx_conv_blur_dst_end; } } else { @@ -1363,7 +1553,7 @@ glx_blur_dst(session_t *ps, int dx, int dy, int width, int height, float z, ret = true; -glx_blur_dst_end: +glx_conv_blur_dst_end: #ifdef CONFIG_VSYNC_OPENGL_FBO glBindFramebuffer(GL_FRAMEBUFFER, 0); #endif @@ -1378,6 +1568,261 @@ glx_blur_dst(session_t *ps, int dx, int dy, int width, int height, float z, free_glx_bc(ps, pbc); } + return ret; +} + +bool +glx_kawase_blur_dst(session_t *ps, int dx, int dy, int width, int height, float z, + XserverRegion reg_tgt, const reg_data_t *pcache_reg, + glx_blur_cache_t *pbc) { + 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 + + GLenum tex_tgt = GL_TEXTURE_RECTANGLE; + if (ps->psglx->has_texture_non_power_of_two) + tex_tgt = GL_TEXTURE_2D; + + // 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]; + + // FIXME.kawase: Allocate only as many textures as needed + for (int i = 1; i < 6; i++) { + if (!pbc->textures[i]) + pbc->textures[i] = glx_gen_texture(ps, tex_tgt, mwidth, mheight); + } + + pbc->width = mwidth; + pbc->height = mheight; + + if (!pbc->fbo) + glGenFramebuffers(1, &pbc->fbo); + const GLuint fbo = pbc->fbo; + + if (!tex_scr || !pbc->textures[1] || !pbc->textures[2] || !pbc->textures[3] || !pbc->textures[4] || !pbc->textures[5]) { + printf_errf("(): Failed to allocate textures."); + goto glx_kawase_blur_dst_end; + } + if (!fbo) { + printf_errf("(): Failed to allocate framebuffer."); + goto glx_kawase_blur_dst_end; + } + + // 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); + + // Texture scaling factor + GLfloat texfac_x = 1.0f, texfac_y = 1.0f; + if (GL_TEXTURE_2D == tex_tgt) { + texfac_x /= mwidth; + texfac_y /= mheight; + } + + // Paint it back + //if (more_passes) { + glDisable(GL_STENCIL_TEST); + glDisable(GL_SCISSOR_TEST); + //} + + // First pass(es): Kawase Downsample + for (int i = 1; i <= ps->o.blur_strength_iterations; i++) { + const glx_blur_pass_t *down_pass = &ps->psglx->blur_passes[0]; + assert(down_pass->prog); + + int tex_width = mwidth / (1 << (i-1)), tex_height = mheight / (1 << (i-1)); + GLuint tex_src2 = pbc->textures[i - 1]; + GLuint tex_dest = pbc->textures[i]; + + assert(tex_src2); + assert(tex_dest); + glBindTexture(tex_tgt, tex_src2); + + static const GLenum DRAWBUFS[2] = { GL_COLOR_ATTACHMENT0 }; + glBindFramebuffer(GL_FRAMEBUFFER, fbo); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, tex_dest, 0); + glDrawBuffers(1, DRAWBUFS); + if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { + printf_errf("(): Framebuffer attachment failed."); + goto glx_kawase_blur_dst_end; + } + + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + glUseProgram(down_pass->prog); + if (down_pass->unifm_offset >= 0) + glUniform1f(down_pass->unifm_offset, ps->o.blur_strength_offset); + if (down_pass->unifm_halfpixel >= 0) + glUniform2f(down_pass->unifm_halfpixel, 0.5 / tex_width, 0.5 / tex_height); + + // Start actual rendering + 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) / (1 << (i-1)); + GLfloat rdy = (mheight - crect.y + mdy) / (1 << (i-1)); + GLfloat rdxe = rdx + (crect.width / (1 << (i-1))); + GLfloat rdye = rdy - (crect.height / (1 << (i-1))); + +#ifdef DEBUG_GLX + printf_dbgf("(): Downsample Pass %d: %f, %f, %f, %f -> %f, %f, %f, %f\n", i, rx, ry, rxe, rye, rdx, rdy, rdxe, rdye); +#endif + + glTexCoord2f(rx, ry); + glVertex3f(rdx, rdy, z); + + glTexCoord2f(rxe, ry); + glVertex3f(rdxe, rdy, z); + + glTexCoord2f(rxe, rye); + glVertex3f(rdxe, rdye, z); + + glTexCoord2f(rx, rye); + glVertex3f(rdx, rdye, z); + } + P_PAINTREG_END(); + } + + // Second pass(es): Kawase Upsample + for (int i = ps->o.blur_strength_iterations; i >= 1; i--) { + const glx_blur_pass_t *up_pass = &ps->psglx->blur_passes[1]; + bool is_last = (i == 1); + assert(up_pass->prog); + + int tex_width = mwidth / (1 << (i-1)), tex_height = mheight / (1 << (i-1)); + GLuint tex_src2 = pbc->textures[i]; + GLuint tex_dest = pbc->textures[i - 1]; + + assert(tex_src2); + assert(tex_dest); + glBindTexture(tex_tgt, tex_src2); + + if (!is_last) { + static const GLenum DRAWBUFS[2] = { GL_COLOR_ATTACHMENT0 }; + glBindFramebuffer(GL_FRAMEBUFFER, fbo); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, tex_dest, 0); + glDrawBuffers(1, DRAWBUFS); + if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { + printf_errf("(): Framebuffer attachment failed."); + goto glx_kawase_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); + } + + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + glUseProgram(up_pass->prog); + if (up_pass->unifm_offset >= 0) + glUniform1f(up_pass->unifm_offset, ps->o.blur_strength_offset); + if (up_pass->unifm_halfpixel >= 0) + glUniform2f(up_pass->unifm_halfpixel, 0.5 / tex_width, 0.5 / tex_height); + + // Start actual rendering + P_PAINTREG_START(); + { + const GLfloat rx = ((crect.x - mdx) * texfac_x) / (1 << (i-1)); + const GLfloat ry = ((mheight - (crect.y - mdy)) * texfac_y) / (1 << (i-1)); + const GLfloat rxe = rx + (crect.width * texfac_x) / (1 << (i-1)); + const GLfloat rye = ry - (crect.height * texfac_y) / (1 << (i-1)); + GLfloat rdx = crect.x - mdx; + GLfloat rdy = mheight - crect.y + mdy; + GLfloat rdxe = rdx + crect.width; + GLfloat rdye = rdy - crect.height; + + if (is_last) { + rdx = crect.x; + rdy = ps->root_height - crect.y; + rdxe = rdx + crect.width; + rdye = rdy - crect.height; + } + +#ifdef DEBUG_GLX + printf_dbgf("(): Upsample Pass %d: %f, %f, %f, %f -> %f, %f, %f, %f\n", i, rx, ry, rxe, rye, rdx, rdy, rdxe, rdye); +#endif + + glTexCoord2f(rx, ry); + glVertex3f(rdx, rdy, z); + + glTexCoord2f(rxe, ry); + glVertex3f(rdxe, rdy, z); + + glTexCoord2f(rxe, rye); + glVertex3f(rdxe, rdye, z); + + glTexCoord2f(rx, rye); + glVertex3f(rdx, rdye, z); + } + P_PAINTREG_END(); + } + + glUseProgram(0); + ret = true; + +glx_kawase_blur_dst_end: + glBindFramebuffer(GL_FRAMEBUFFER, 0); + glBindTexture(tex_tgt, 0); + 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); + } + + return ret; +} + +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, + glx_blur_cache_t *pbc) { + assert(ps->psglx->blur_passes[0].prog); + + bool ret; + switch (ps->o.blur_method) { + case BLRMTHD_CONV: + ret = glx_conv_blur_dst(ps, dx, dy, width, height, z, + factor_center, reg_tgt, pcache_reg, pbc); + break; + case BLRMTHD_KAWASE: + ret = glx_kawase_blur_dst(ps, dx, dy, width, height, z, + reg_tgt, pcache_reg, pbc); + break; + default: + ret = false; + break; + } + glx_check_err(ps); return ret; From 61d588b1d6a80de7a8ced3c669f25a61e2e073bb Mon Sep 17 00:00:00 2001 From: Bernd Busse Date: Tue, 20 Feb 2018 20:36:34 +0100 Subject: [PATCH 2/8] Kawase Blur: Fixed wrong texture coordinates --- src/opengl.c | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/src/opengl.c b/src/opengl.c index 120448af..2d7e9123 100644 --- a/src/opengl.c +++ b/src/opengl.c @@ -1645,6 +1645,7 @@ glx_kawase_blur_dst(session_t *ps, int dx, int dy, int width, int height, float // First pass(es): Kawase Downsample for (int i = 1; i <= ps->o.blur_strength_iterations; i++) { const glx_blur_pass_t *down_pass = &ps->psglx->blur_passes[0]; + bool is_first = (i == 1); assert(down_pass->prog); int tex_width = mwidth / (1 << (i-1)), tex_height = mheight / (1 << (i-1)); @@ -1675,14 +1676,21 @@ glx_kawase_blur_dst(session_t *ps, int dx, int dy, int width, int height, float // Start actual rendering 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) / (1 << (i-1)); - GLfloat rdy = (mheight - crect.y + mdy) / (1 << (i-1)); - GLfloat rdxe = rdx + (crect.width / (1 << (i-1))); - GLfloat rdye = rdy - (crect.height / (1 << (i-1))); + GLfloat rx = ((crect.x - mdx) * texfac_x) / (1 << (i-2)); + GLfloat ry = ((mheight - (crect.y - mdy)) * texfac_y) / (1 << (i-2)); + GLfloat rxe = rx + (crect.width * texfac_x) / (1 << (i-2)); + GLfloat rye = ry - (crect.height * texfac_y) / (1 << (i-2)); + const GLfloat rdx = (crect.x - mdx) / (1 << (i-1)); + const GLfloat rdy = (mheight - crect.y + mdy) / (1 << (i-1)); + const GLfloat rdxe = rdx + (crect.width / (1 << (i-1))); + const GLfloat rdye = rdy - (crect.height / (1 << (i-1))); + + if (is_first) { + rx = (crect.x - mdx) * texfac_x; + ry = (mheight - (crect.y - mdy)) * texfac_y; + rxe = rx + crect.width * texfac_x; + rye = ry - crect.height * texfac_y; + } #ifdef DEBUG_GLX printf_dbgf("(): Downsample Pass %d: %f, %f, %f, %f -> %f, %f, %f, %f\n", i, rx, ry, rxe, rye, rdx, rdy, rdxe, rdye); @@ -1751,10 +1759,10 @@ glx_kawase_blur_dst(session_t *ps, int dx, int dy, int width, int height, float const GLfloat ry = ((mheight - (crect.y - mdy)) * texfac_y) / (1 << (i-1)); const GLfloat rxe = rx + (crect.width * texfac_x) / (1 << (i-1)); const GLfloat rye = ry - (crect.height * texfac_y) / (1 << (i-1)); - GLfloat rdx = crect.x - mdx; - GLfloat rdy = mheight - crect.y + mdy; - GLfloat rdxe = rdx + crect.width; - GLfloat rdye = rdy - crect.height; + GLfloat rdx = (crect.x - mdx) / (1 << (i-2)); + GLfloat rdy = (mheight - crect.y + mdy) / (1 << (i-2)); + GLfloat rdxe = rdx + (crect.width / (1 << (i-2))); + GLfloat rdye = rdy - (crect.height / (1 << (i-2))); if (is_last) { rdx = crect.x; From c2768ec6b6f8a0d739bc677581721cd5804457ea Mon Sep 17 00:00:00 2001 From: Bernd Busse Date: Tue, 20 Feb 2018 21:55:02 +0100 Subject: [PATCH 3/8] Kawase Blur: render to properly scaled textures --- src/common.h | 12 ++--- src/opengl.c | 124 +++++++++++++++++++++++++-------------------------- 2 files changed, 67 insertions(+), 69 deletions(-) diff --git a/src/common.h b/src/common.h index e1571b08..701bd784 100644 --- a/src/common.h +++ b/src/common.h @@ -474,16 +474,18 @@ typedef struct { GLuint frag_shader; /// GLSL program for blur. GLuint prog; - /// Location of uniform "offset_x" in blur GLSL program. + /// Location of uniform "offset_x" in conv-blur GLSL program. GLint unifm_offset_x; - /// Location of uniform "offset_y" in blur GLSL program. + /// Location of uniform "offset_y" in conv-blur GLSL program. GLint unifm_offset_y; - /// Location of uniform "factor_center" in blur GLSL program. + /// Location of uniform "factor_center" in conv-blur GLSL program. GLint unifm_factor_center; - /// Location of uniform "offset" in blur GLSL program. + /// Location of uniform "offset" in kawase-blur GLSL program. GLint unifm_offset; - /// Location of uniform "halfpixel" in blur GLSL program. + /// Location of uniform "halfpixel" in kawase-blur GLSL program. GLint unifm_halfpixel; + /// Location of uniform "fulltex" in kawase-blur GLSL program. + GLint unifm_fulltex; } glx_blur_pass_t; typedef struct { diff --git a/src/opengl.c b/src/opengl.c index 2d7e9123..d0dd9b7a 100644 --- a/src/opengl.c +++ b/src/opengl.c @@ -560,34 +560,40 @@ glx_init_kawase_blur(session_t *ps) { "%s" // extensions "uniform float offset;\n" "uniform vec2 halfpixel;\n" + "uniform vec2 fulltex;\n" "uniform %s tex_scr;\n" // sampler2D | sampler2DRect + "vec4 clamp_tex(vec2 uv)\n" + "{\n" + " return %s(tex_scr, clamp(uv, vec2(0), fulltex));\n" // texture2D | texture2DRect + "}\n" + "\n" "void main()\n" "{\n" - " vec2 uv = gl_TexCoord[0].xy;\n" - " \n"; + " vec2 uv = (gl_TexCoord[0].xy / fulltex);\n" + "\n"; // Fragment shader (Dual Kawase Blur) - Downsample static const char *FRAG_SHADER_KAWASE_DOWN = - " vec4 sum = %1$s(tex_scr, uv) * 4.0;\n" // texture | texture2D - " sum += %1$s(tex_scr, uv - halfpixel.xy * offset);\n" - " sum += %1$s(tex_scr, uv + halfpixel.xy * offset);\n" - " sum += %1$s(tex_scr, uv + vec2(halfpixel.x, -halfpixel.y) * offset);\n" - " sum += %1$s(tex_scr, uv - vec2(halfpixel.x, -halfpixel.y) * offset);\n" - " \n" + " vec4 sum = clamp_tex(uv) * 4.0;\n" + " sum += clamp_tex(uv - halfpixel.xy * offset);\n" + " sum += clamp_tex(uv + halfpixel.xy * offset);\n" + " sum += clamp_tex(uv + vec2(halfpixel.x, -halfpixel.y) * offset);\n" + " sum += clamp_tex(uv - vec2(halfpixel.x, -halfpixel.y) * offset);\n" + "\n" " gl_FragColor = sum / 8.0;\n" "}\n"; // Fragment shader (Dual Kawase Blur) - Upsample static const char *FRAG_SHADER_KAWASE_UP = - " vec4 sum = %1$s(tex_scr, uv + vec2(-halfpixel.x * 2.0, 0.0) * offset);\n" // texture | texture2D - " sum += %1$s(tex_scr, uv + vec2(-halfpixel.x, halfpixel.y) * offset) * 2.0;\n" - " sum += %1$s(tex_scr, uv + vec2(0.0, halfpixel.y * 2.0) * offset);\n" - " sum += %1$s(tex_scr, uv + vec2(halfpixel.x, halfpixel.y) * offset) * 2.0;\n" - " sum += %1$s(tex_scr, uv + vec2(halfpixel.x * 2.0, 0.0) * offset);\n" - " sum += %1$s(tex_scr, uv + vec2(halfpixel.x, -halfpixel.y) * offset) * 2.0;\n" - " sum += %1$s(tex_scr, uv + vec2(0.0, -halfpixel.y * 2.0) * offset);\n" - " sum += %1$s(tex_scr, uv + vec2(-halfpixel.x, -halfpixel.y) * offset) * 2.0;\n" - " \n" + " vec4 sum = clamp_tex(uv + vec2(-halfpixel.x * 2.0, 0.0) * offset);\n" + " sum += clamp_tex(uv + vec2(-halfpixel.x, halfpixel.y) * offset) * 2.0;\n" + " sum += clamp_tex(uv + vec2(0.0, halfpixel.y * 2.0) * offset);\n" + " sum += clamp_tex(uv + vec2(halfpixel.x, halfpixel.y) * offset) * 2.0;\n" + " sum += clamp_tex(uv + vec2(halfpixel.x * 2.0, 0.0) * offset);\n" + " sum += clamp_tex(uv + vec2(halfpixel.x, -halfpixel.y) * offset) * 2.0;\n" + " sum += clamp_tex(uv + vec2(0.0, -halfpixel.y * 2.0) * offset);\n" + " sum += clamp_tex(uv + vec2(-halfpixel.x, -halfpixel.y) * offset) * 2.0;\n" + "\n" " gl_FragColor = sum / 12.0;\n" "}\n"; @@ -603,7 +609,7 @@ glx_init_kawase_blur(session_t *ps) { // Build kawase downsample shader glx_blur_pass_t *down_pass = &ps->psglx->blur_passes[0]; { - int len = strlen(FRAG_SHADER_PREFIX) + strlen(extension) + (strlen(FRAG_SHADER_KAWASE_DOWN) + strlen(texture_func)) * 5 + 1; + int len = strlen(FRAG_SHADER_PREFIX) + strlen(extension) + strlen(sampler_type) + strlen(texture_func) + strlen(FRAG_SHADER_KAWASE_DOWN) + 1; char *shader_str = calloc(len, sizeof(char)); if (!shader_str) { printf_errf("(): Failed to allocate %d bytes for shader string.", len); @@ -611,11 +617,11 @@ glx_init_kawase_blur(session_t *ps) { } char *pc = shader_str; - sprintf(pc, FRAG_SHADER_PREFIX, extension, sampler_type); + sprintf(pc, FRAG_SHADER_PREFIX, extension, sampler_type, texture_func); pc += strlen(pc); assert(strlen(shader_str) < len); - sprintf(pc, FRAG_SHADER_KAWASE_DOWN, texture_func); + sprintf(pc, FRAG_SHADER_KAWASE_DOWN); assert(strlen(shader_str) < len); #ifdef DEBUG_GLX printf_dbgf("(): Generated kawase downsample shader:\n%s\n", shader_str); @@ -644,13 +650,14 @@ glx_init_kawase_blur(session_t *ps) { } P_GET_UNIFM_LOC("offset", unifm_offset); P_GET_UNIFM_LOC("halfpixel", unifm_halfpixel); + P_GET_UNIFM_LOC("fulltex", unifm_fulltex); #undef P_GET_UNIFM_LOC } // Build kawase downsample shader glx_blur_pass_t *up_pass = &ps->psglx->blur_passes[1]; { - int len = strlen(FRAG_SHADER_PREFIX) + strlen(extension) + (strlen(FRAG_SHADER_KAWASE_UP) + strlen(texture_func)) * 8 + 1; + int len = strlen(FRAG_SHADER_PREFIX) + strlen(extension) + strlen(sampler_type) + strlen(texture_func) + strlen(FRAG_SHADER_KAWASE_UP) + 1; char *shader_str = calloc(len, sizeof(char)); if (!shader_str) { printf_errf("(): Failed to allocate %d bytes for shader string.", len); @@ -658,11 +665,11 @@ glx_init_kawase_blur(session_t *ps) { } char *pc = shader_str; - sprintf(pc, FRAG_SHADER_PREFIX, extension, sampler_type); + sprintf(pc, FRAG_SHADER_PREFIX, extension, sampler_type, texture_func); pc += strlen(pc); assert(strlen(shader_str) < len); - sprintf(pc, FRAG_SHADER_KAWASE_UP, texture_func); + sprintf(pc, FRAG_SHADER_KAWASE_UP); assert(strlen(shader_str) < len); #ifdef DEBUG_GLX printf_dbgf("(): Generated kawase upsample shader:\n%s\n", shader_str); @@ -691,6 +698,7 @@ glx_init_kawase_blur(session_t *ps) { } P_GET_UNIFM_LOC("offset", unifm_offset); P_GET_UNIFM_LOC("halfpixel", unifm_halfpixel); + P_GET_UNIFM_LOC("fulltex", unifm_fulltex); #undef P_GET_UNIFM_LOC } @@ -714,6 +722,8 @@ glx_init_blur(session_t *ps) { return glx_init_conv_blur(ps); case BLRMTHD_KAWASE: return glx_init_kawase_blur(ps); + default: + return false; } #else printf_errf("(): GLSL support not compiled in. Cannot do blur with GLX backend."); @@ -1605,7 +1615,7 @@ glx_kawase_blur_dst(session_t *ps, int dx, int dy, int width, int height, float // FIXME.kawase: Allocate only as many textures as needed for (int i = 1; i < 6; i++) { if (!pbc->textures[i]) - pbc->textures[i] = glx_gen_texture(ps, tex_tgt, mwidth, mheight); + pbc->textures[i] = glx_gen_texture(ps, tex_tgt, mwidth / (1 << (i-1)), mheight / (1 << (i-1))); } pbc->width = mwidth; @@ -1629,23 +1639,13 @@ glx_kawase_blur_dst(session_t *ps, int dx, int dy, int width, int height, float glBindTexture(tex_tgt, tex_scr); glx_copy_region_to_tex(ps, tex_tgt, mdx, mdy, mdx, mdy, mwidth, mheight); - // Texture scaling factor - GLfloat texfac_x = 1.0f, texfac_y = 1.0f; - if (GL_TEXTURE_2D == tex_tgt) { - texfac_x /= mwidth; - texfac_y /= mheight; - } - // Paint it back - //if (more_passes) { - glDisable(GL_STENCIL_TEST); - glDisable(GL_SCISSOR_TEST); - //} + glDisable(GL_STENCIL_TEST); + glDisable(GL_SCISSOR_TEST); // First pass(es): Kawase Downsample for (int i = 1; i <= ps->o.blur_strength_iterations; i++) { const glx_blur_pass_t *down_pass = &ps->psglx->blur_passes[0]; - bool is_first = (i == 1); assert(down_pass->prog); int tex_width = mwidth / (1 << (i-1)), tex_height = mheight / (1 << (i-1)); @@ -1672,41 +1672,32 @@ glx_kawase_blur_dst(session_t *ps, int dx, int dy, int width, int height, float glUniform1f(down_pass->unifm_offset, ps->o.blur_strength_offset); if (down_pass->unifm_halfpixel >= 0) glUniform2f(down_pass->unifm_halfpixel, 0.5 / tex_width, 0.5 / tex_height); + if (down_pass->unifm_fulltex >= 0) + glUniform2f(down_pass->unifm_fulltex, tex_width, tex_height); // Start actual rendering P_PAINTREG_START(); { - GLfloat rx = ((crect.x - mdx) * texfac_x) / (1 << (i-2)); - GLfloat ry = ((mheight - (crect.y - mdy)) * texfac_y) / (1 << (i-2)); - GLfloat rxe = rx + (crect.width * texfac_x) / (1 << (i-2)); - GLfloat rye = ry - (crect.height * texfac_y) / (1 << (i-2)); - const GLfloat rdx = (crect.x - mdx) / (1 << (i-1)); - const GLfloat rdy = (mheight - crect.y + mdy) / (1 << (i-1)); - const GLfloat rdxe = rdx + (crect.width / (1 << (i-1))); - const GLfloat rdye = rdy - (crect.height / (1 << (i-1))); - - if (is_first) { - rx = (crect.x - mdx) * texfac_x; - ry = (mheight - (crect.y - mdy)) * texfac_y; - rxe = rx + crect.width * texfac_x; - rye = ry - crect.height * texfac_y; - } + const GLfloat rx = crect.x - mdx; + const GLfloat ry = mheight - (crect.y - mdy); + const GLfloat rxe = rx + crect.width; + const GLfloat rye = ry - crect.height; #ifdef DEBUG_GLX printf_dbgf("(): Downsample Pass %d: %f, %f, %f, %f -> %f, %f, %f, %f\n", i, rx, ry, rxe, rye, rdx, rdy, rdxe, rdye); #endif glTexCoord2f(rx, ry); - glVertex3f(rdx, rdy, z); + glVertex3f(rx, ry, z); glTexCoord2f(rxe, ry); - glVertex3f(rdxe, rdy, z); + glVertex3f(rxe, ry, z); glTexCoord2f(rxe, rye); - glVertex3f(rdxe, rdye, z); + glVertex3f(rxe, rye, z); glTexCoord2f(rx, rye); - glVertex3f(rdx, rdye, z); + glVertex3f(rx, rye, z); } P_PAINTREG_END(); } @@ -1717,7 +1708,10 @@ glx_kawase_blur_dst(session_t *ps, int dx, int dy, int width, int height, float bool is_last = (i == 1); assert(up_pass->prog); - int tex_width = mwidth / (1 << (i-1)), tex_height = mheight / (1 << (i-1)); + int tex_width = mwidth / (1 << (i-2)), tex_height = mheight / (1 << (i-2)); + if (is_last) { + tex_width = mwidth, tex_height = mheight; + } GLuint tex_src2 = pbc->textures[i]; GLuint tex_dest = pbc->textures[i - 1]; @@ -1751,18 +1745,20 @@ glx_kawase_blur_dst(session_t *ps, int dx, int dy, int width, int height, float glUniform1f(up_pass->unifm_offset, ps->o.blur_strength_offset); if (up_pass->unifm_halfpixel >= 0) glUniform2f(up_pass->unifm_halfpixel, 0.5 / tex_width, 0.5 / tex_height); + if (up_pass->unifm_fulltex >= 0) + glUniform2f(up_pass->unifm_fulltex, tex_width, tex_height); // Start actual rendering P_PAINTREG_START(); { - const GLfloat rx = ((crect.x - mdx) * texfac_x) / (1 << (i-1)); - const GLfloat ry = ((mheight - (crect.y - mdy)) * texfac_y) / (1 << (i-1)); - const GLfloat rxe = rx + (crect.width * texfac_x) / (1 << (i-1)); - const GLfloat rye = ry - (crect.height * texfac_y) / (1 << (i-1)); - GLfloat rdx = (crect.x - mdx) / (1 << (i-2)); - GLfloat rdy = (mheight - crect.y + mdy) / (1 << (i-2)); - GLfloat rdxe = rdx + (crect.width / (1 << (i-2))); - GLfloat rdye = rdy - (crect.height / (1 << (i-2))); + const GLfloat rx = crect.x - mdx; + const GLfloat ry = mheight - (crect.y - mdy); + const GLfloat rxe = rx + crect.width; + const GLfloat rye = ry - crect.height; + GLfloat rdx = rx; + GLfloat rdy = ry; + GLfloat rdxe = rxe; + GLfloat rdye = rye; if (is_last) { rdx = crect.x; From ae9f27da1337ac2f78eb2f5c0c198614ef9173dd Mon Sep 17 00:00:00 2001 From: Bernd Busse Date: Thu, 22 Feb 2018 19:53:47 +0100 Subject: [PATCH 4/8] Fix config file parsing --- src/common.h | 17 ++--------------- src/compton.c | 7 +++---- 2 files changed, 5 insertions(+), 19 deletions(-) diff --git a/src/common.h b/src/common.h index 701bd784..8daa6584 100644 --- a/src/common.h +++ b/src/common.h @@ -1707,20 +1707,7 @@ parse_blur_method(session_t *ps, const char *str) { * Parse a blur_strength option argument. */ static inline bool -parse_blur_strength(session_t *ps, const char *str) { - const char *endptr = NULL; - long level = strtol(str, (char **) &endptr, 0); - if (!endptr || endptr == str) { - printf_errf("(\"%s\"): Invalid number.", str); - return false; - } - while (isspace(*endptr)) - ++endptr; - if (*endptr) { - printf_errf("(\"%s\"): Trailing characters.", str); - return false; - } - +parse_blur_strength(session_t *ps, const int level) { switch (level) { case 1: ps->o.blur_strength_iterations = 1; @@ -1783,7 +1770,7 @@ parse_blur_strength(session_t *ps, const char *str) { ps->o.blur_strength_offset = 8.0; break; default: - printf_errf("(\"%s\"): Invalid blur_strength argument. Needs to be a number between 1 and 15.", str); + printf_errf("(\"%d\"): Invalid blur_strength argument. Needs to be a number between 1 and 15.", level); return false; } diff --git a/src/compton.c b/src/compton.c index 7c847911..83226fc9 100644 --- a/src/compton.c +++ b/src/compton.c @@ -5645,9 +5645,8 @@ parse_config(session_t *ps, struct options_tmp *pcfgtmp) { && !parse_conv_kern_lst(ps, sval, ps->o.blur_kerns, MAX_BLUR_PASS)) exit(1); // --blur-strength - //lcfg_lookup_int(&cfg, "blur-strength", &ps->o.blur_strength); - if (config_lookup_string(&cfg, "blur-strength", &sval) - && !parse_blur_strength(ps, sval)) + if (lcfg_lookup_int(&cfg, "blur-strength", &ival) + && !parse_blur_strength(ps, ival)) exit(1); // --resize-damage lcfg_lookup_int(&cfg, "resize-damage", &ps->o.resize_damage); @@ -6063,7 +6062,7 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { break; case 322: // --blur-strength - if (!parse_blur_strength(ps, optarg)) + if (!parse_blur_strength(ps, strtol(optarg, NULL, 0))) exit(1); break; P_CASEBOOL(731, reredir_on_root_change); From 34aa5a8cc7ee0af78aa61bad1af5e6b3a60860e9 Mon Sep 17 00:00:00 2001 From: Bernd Busse Date: Thu, 22 Feb 2018 20:19:19 +0100 Subject: [PATCH 5/8] Update Readme and Manpage --- README.md | 4 ++++ man/compton.1.asciidoc | 8 ++++++++ src/compton.c | 18 ++++++++---------- 3 files changed, 20 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 05c30a45..1db8dc4e 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,10 @@ __Compton__ is a compositor for X, and a fork of __xcompmgr-dana__. +This branch includes a new blur method: The multi-pass **dual kawase blur**! +Use it with the `--blur-method kawase` and `--blur-strength LEVEL` options. +Only works with the `glx` backend! + I was frustrated by the low amount of standalone lightweight compositors. Compton was forked from Dana Jansens' fork of xcompmgr and refactored. I fixed whatever bug I found, and added features I wanted. Things seem stable, but don't diff --git a/man/compton.1.asciidoc b/man/compton.1.asciidoc index 95729499..6a88f909 100644 --- a/man/compton.1.asciidoc +++ b/man/compton.1.asciidoc @@ -205,7 +205,15 @@ OPTIONS *--blur-background-fixed*:: Use fixed blur strength rather than adjusting according to window opacity. +*--blur-method* 'ALGORITHM':: + Specify the algorithm for background blur. It is either one of: `convolution` (default), `kawase`. + +*--blur-strength* 'LEVEL':: + Only valid for *--blur-method kawase*! + The strength of the kawase blur as an integer between 1 and 15. + *--blur-kern* 'MATRIX':: + Only valid for *--blur-method convolution*! Specify the blur convolution kernel, with the following format: + ---- diff --git a/src/compton.c b/src/compton.c index 83226fc9..a6760404 100644 --- a/src/compton.c +++ b/src/compton.c @@ -4706,11 +4706,14 @@ usage(int ret) { " Use fixed blur strength instead of adjusting according to window\n" " opacity.\n" "\n" - // FIXME.kawse: kawase algorithm only available when compiled with GLX support "--blur-method algorithm\n" " Specify the algorithm for background blur. It is either one of:\n" " convolution (default), kawase\n" "\n" + "--blur-strength level\n" + " Only valid for '--blur-method kawase'!\n" + " The strength of the kawase blur as an integer between 1 and 15.\n" + "\n" "--blur-kern matrix\n" " Only valid for '--blur-method convolution'!\n" " Specify the blur convolution kernel, with the following format:\n" @@ -4726,11 +4729,6 @@ usage(int ret) { " 7x7box, 3x3gaussian, 5x5gaussian, 7x7gaussian, 9x9gaussian,\n" " 11x11gaussian.\n" "\n" - // FIXME.kawse: kawase algorithm only available when compiled with GLX support - "--blur-strength integer\n" - " Only valid for '--blur-method kawase'!\n" - " The strength of the kawase blur as an integer between 1 and 15.\n" - "\n" "--blur-background-exclude condition\n" " Exclude conditions for background blur.\n" "\n" @@ -5640,14 +5638,14 @@ parse_config(session_t *ps, struct options_tmp *pcfgtmp) { if (config_lookup_string(&cfg, "blur-method", &sval) && !parse_blur_method(ps, sval)) exit(1); - // --blur-kern - if (config_lookup_string(&cfg, "blur-kern", &sval) - && !parse_conv_kern_lst(ps, sval, ps->o.blur_kerns, MAX_BLUR_PASS)) - exit(1); // --blur-strength if (lcfg_lookup_int(&cfg, "blur-strength", &ival) && !parse_blur_strength(ps, ival)) exit(1); + // --blur-kern + if (config_lookup_string(&cfg, "blur-kern", &sval) + && !parse_conv_kern_lst(ps, sval, ps->o.blur_kerns, MAX_BLUR_PASS)) + exit(1); // --resize-damage lcfg_lookup_int(&cfg, "resize-damage", &ps->o.resize_damage); // --glx-no-stencil From 964085c6f2dc19ea550230b7f8a8e8176d6ab505 Mon Sep 17 00:00:00 2001 From: Bernd Busse Date: Thu, 22 Feb 2018 20:37:39 +0100 Subject: [PATCH 6/8] Add warning when not using glx backend --- man/compton.1.asciidoc | 2 ++ src/compton.c | 6 ++++++ 2 files changed, 8 insertions(+) diff --git a/man/compton.1.asciidoc b/man/compton.1.asciidoc index 6a88f909..cb7e6541 100644 --- a/man/compton.1.asciidoc +++ b/man/compton.1.asciidoc @@ -207,6 +207,8 @@ OPTIONS *--blur-method* 'ALGORITHM':: Specify the algorithm for background blur. It is either one of: `convolution` (default), `kawase`. ++ +Note: `kawase` only works with the `glx` backend. *--blur-strength* 'LEVEL':: Only valid for *--blur-method kawase*! diff --git a/src/compton.c b/src/compton.c index a6760404..506dccab 100644 --- a/src/compton.c +++ b/src/compton.c @@ -6127,6 +6127,12 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { ps->o.track_leader = true; } + // Blur method kawase is not compatible with the xrender backend + if (ps->o.backend != BKEND_GLX && ps->o.blur_method == BLRMTHD_KAWASE) { + printf_errf("(): Blur method 'kawase' is incompatible with the XRender backend. Fall back to default.\n"); + ps->o.blur_method = BLRMTHD_CONV; + } + // Fill default blur kernel if (ps->o.blur_background && (BLRMTHD_CONV == ps->o.blur_method) && !ps->o.blur_kerns[0]) { // Convolution filter parameter (box blur) From 6929e29b9fdf7ac90d3e55007ee0e34974b0ce5d Mon Sep 17 00:00:00 2001 From: Bernd Busse Date: Thu, 22 Feb 2018 21:49:53 +0100 Subject: [PATCH 7/8] Add additional blur levels and fix downscaling --- man/compton.1.asciidoc | 2 +- src/common.h | 107 ++++++++++++++--------------------------- src/compton.c | 5 +- src/opengl.c | 29 ++++++++--- 4 files changed, 60 insertions(+), 83 deletions(-) diff --git a/man/compton.1.asciidoc b/man/compton.1.asciidoc index cb7e6541..7b1b9811 100644 --- a/man/compton.1.asciidoc +++ b/man/compton.1.asciidoc @@ -212,7 +212,7 @@ Note: `kawase` only works with the `glx` backend. *--blur-strength* 'LEVEL':: Only valid for *--blur-method kawase*! - The strength of the kawase blur as an integer between 1 and 15. + The strength of the kawase blur as an integer between 1 and 20. Defaults to 5. *--blur-kern* 'MATRIX':: Only valid for *--blur-method convolution*! diff --git a/src/common.h b/src/common.h index 8daa6584..e41139f1 100644 --- a/src/common.h +++ b/src/common.h @@ -229,7 +229,7 @@ #define CGLX_MAX_BUFFER_AGE 5 /// @brief Maximum passes for blur. -#define MAX_BLUR_PASS 5 +#define MAX_BLUR_PASS 6 // Window flags @@ -491,9 +491,8 @@ typedef struct { typedef struct { /// Framebuffer used for blurring. GLuint fbo; - // FIXME.kawase: Set maximum number of textures for kawase blur /// Textures used for blurring. - GLuint textures[6]; // 4 + 2 + GLuint textures[MAX_BLUR_PASS]; /// Width of the textures. int width; /// Height of the textures. @@ -552,6 +551,11 @@ struct _timeout_t; struct _win; +typedef struct { + int iterations; + float offset; +} blur_strength_t; + typedef struct _c2_lptr c2_lptr_t; /// Structure representing all options. @@ -726,8 +730,7 @@ typedef struct _options_t { /// Blur convolution kernel. XFixed *blur_kerns[MAX_BLUR_PASS]; /// Blur strength. - int blur_strength_iterations; - float blur_strength_offset; + blur_strength_t blur_strength; /// 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 @@ -1708,72 +1711,35 @@ parse_blur_method(session_t *ps, const char *str) { */ static inline bool parse_blur_strength(session_t *ps, const int level) { - switch (level) { - case 1: - ps->o.blur_strength_iterations = 1; - ps->o.blur_strength_offset = 1.5; - break; - case 2: - ps->o.blur_strength_iterations = 1; - ps->o.blur_strength_offset = 2.0; - break; - case 3: - ps->o.blur_strength_iterations = 2; - ps->o.blur_strength_offset = 2.5; - break; - case 4: - ps->o.blur_strength_iterations = 2; - ps->o.blur_strength_offset = 3.0; - break; - case 5: - ps->o.blur_strength_iterations = 3; - ps->o.blur_strength_offset = 2.6; - break; - case 6: - ps->o.blur_strength_iterations = 3; - ps->o.blur_strength_offset = 3.2; - break; - case 7: - ps->o.blur_strength_iterations = 3; - ps->o.blur_strength_offset = 3.8; - break; - case 8: - ps->o.blur_strength_iterations = 3; - ps->o.blur_strength_offset = 4.4; - break; - case 9: - ps->o.blur_strength_iterations = 3; - ps->o.blur_strength_offset = 5.0; - break; - case 10: - ps->o.blur_strength_iterations = 4; - ps->o.blur_strength_offset = 3.833; - break; - case 11: - ps->o.blur_strength_iterations = 4; - ps->o.blur_strength_offset = 4.667; - break; - case 12: - ps->o.blur_strength_iterations = 4; - ps->o.blur_strength_offset = 5.5; - break; - case 13: - ps->o.blur_strength_iterations = 4; - ps->o.blur_strength_offset = 6.333; - break; - case 14: - ps->o.blur_strength_iterations = 4; - ps->o.blur_strength_offset = 7.167; - break; - case 15: - ps->o.blur_strength_iterations = 4; - ps->o.blur_strength_offset = 8.0; - break; - default: - printf_errf("(\"%d\"): Invalid blur_strength argument. Needs to be a number between 1 and 15.", level); - return false; + static const blur_strength_t values[20] = { + { .iterations = 1, .offset = 1.5 }, // 1 + { .iterations = 1, .offset = 2.0 }, // 2 + { .iterations = 2, .offset = 2.5 }, // 3 + { .iterations = 2, .offset = 3.0 }, // 4 + { .iterations = 3, .offset = 2.75 }, // 5 + { .iterations = 3, .offset = 3.5 }, // 6 + { .iterations = 3, .offset = 4.25 }, // 7 + { .iterations = 3, .offset = 5.0 }, // 8 + { .iterations = 4, .offset = 3.71429 }, // 9 + { .iterations = 4, .offset = 4.42857 }, // 10 + { .iterations = 4, .offset = 5.14286 }, // 11 + { .iterations = 4, .offset = 5.85714 }, // 12 + { .iterations = 4, .offset = 6.57143 }, // 13 + { .iterations = 4, .offset = 7.28571 }, // 14 + { .iterations = 4, .offset = 8.0 }, // 15 + { .iterations = 5, .offset = 6.0 }, // 16 + { .iterations = 5, .offset = 7.0 }, // 17 + { .iterations = 5, .offset = 8.0 }, // 18 + { .iterations = 5, .offset = 9.0 }, // 19 + { .iterations = 5, .offset = 10.0 }, // 20 + }; + + if (level < 1 || level > 20) { + printf_errf("(\"%d\"): Invalid blur_strength argument. Needs to be a number between 1 and 20.", level); + return false; } + ps->o.blur_strength = values[level - 1]; return true; } @@ -2385,8 +2351,7 @@ free_glx_fbo(session_t *ps, GLuint *pfbo) { */ static inline void free_glx_bc_resize(session_t *ps, glx_blur_cache_t *pbc) { - // FIXME.kawase: Use maximum number of textures - for (int i = 0; i < 6; i++) + for (int i = 0; i < MAX_BLUR_PASS; i++) free_texture_r(ps, &pbc->textures[i]); pbc->width = 0; pbc->height = 0; diff --git a/src/compton.c b/src/compton.c index 506dccab..5f9bdeca 100644 --- a/src/compton.c +++ b/src/compton.c @@ -4712,7 +4712,7 @@ usage(int ret) { "\n" "--blur-strength level\n" " Only valid for '--blur-method kawase'!\n" - " The strength of the kawase blur as an integer between 1 and 15.\n" + " The strength of the kawase blur as an integer between 1 and 20. Defaults to 5.\n" "\n" "--blur-kern matrix\n" " Only valid for '--blur-method convolution'!\n" @@ -7075,8 +7075,7 @@ session_init(session_t *ps_old, int argc, char **argv) { .blur_background_blacklist = NULL, .blur_method = BLRMTHD_CONV, .blur_kerns = { NULL }, - .blur_strength_iterations = 1, - .blur_strength_offset = 1.5, + .blur_strength = { .iterations = 3, .offset = 2.75 }, .inactive_dim = 0.0, .inactive_dim_fixed = false, .invert_color_list = NULL, diff --git a/src/opengl.c b/src/opengl.c index d0dd9b7a..144c08f3 100644 --- a/src/opengl.c +++ b/src/opengl.c @@ -1589,6 +1589,9 @@ glx_kawase_blur_dst(session_t *ps, int dx, int dy, int width, int height, float const bool have_stencil = glIsEnabled(GL_STENCIL_TEST); bool ret = false; + int iterations = ps->o.blur_strength.iterations; + float offset = ps->o.blur_strength.offset; + // Calculate copy region size glx_blur_cache_t ibc = { .width = 0, .height = 0 }; if (!pbc) @@ -1612,8 +1615,12 @@ glx_kawase_blur_dst(session_t *ps, int dx, int dy, int width, int height, float pbc->textures[0] = glx_gen_texture(ps, tex_tgt, mwidth, mheight); GLuint tex_scr = pbc->textures[0]; - // FIXME.kawase: Allocate only as many textures as needed - for (int i = 1; i < 6; i++) { + // Check if we can scale down blur_strength.iterations + while ((mwidth / (1 << (iterations-1))) < 1 || (mheight / (1 << (iterations-1))) < 1) + --iterations; + + assert(iterations < MAX_BLUR_PASS); + for (int i = 1; i <= iterations; i++) { if (!pbc->textures[i]) pbc->textures[i] = glx_gen_texture(ps, tex_tgt, mwidth / (1 << (i-1)), mheight / (1 << (i-1))); } @@ -1625,10 +1632,16 @@ glx_kawase_blur_dst(session_t *ps, int dx, int dy, int width, int height, float glGenFramebuffers(1, &pbc->fbo); const GLuint fbo = pbc->fbo; - if (!tex_scr || !pbc->textures[1] || !pbc->textures[2] || !pbc->textures[3] || !pbc->textures[4] || !pbc->textures[5]) { - printf_errf("(): Failed to allocate textures."); + if (!tex_scr) { + printf_errf("(): Failed to allocate texture."); goto glx_kawase_blur_dst_end; } + for (int i = 1; i <= iterations; i++) { + if (!pbc->textures[i]) { + printf_errf("(): Failed to allocate additional textures."); + goto glx_kawase_blur_dst_end; + } + } if (!fbo) { printf_errf("(): Failed to allocate framebuffer."); goto glx_kawase_blur_dst_end; @@ -1644,7 +1657,7 @@ glx_kawase_blur_dst(session_t *ps, int dx, int dy, int width, int height, float glDisable(GL_SCISSOR_TEST); // First pass(es): Kawase Downsample - for (int i = 1; i <= ps->o.blur_strength_iterations; i++) { + for (int i = 1; i <= iterations; i++) { const glx_blur_pass_t *down_pass = &ps->psglx->blur_passes[0]; assert(down_pass->prog); @@ -1669,7 +1682,7 @@ glx_kawase_blur_dst(session_t *ps, int dx, int dy, int width, int height, float glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); glUseProgram(down_pass->prog); if (down_pass->unifm_offset >= 0) - glUniform1f(down_pass->unifm_offset, ps->o.blur_strength_offset); + glUniform1f(down_pass->unifm_offset, offset); if (down_pass->unifm_halfpixel >= 0) glUniform2f(down_pass->unifm_halfpixel, 0.5 / tex_width, 0.5 / tex_height); if (down_pass->unifm_fulltex >= 0) @@ -1703,7 +1716,7 @@ glx_kawase_blur_dst(session_t *ps, int dx, int dy, int width, int height, float } // Second pass(es): Kawase Upsample - for (int i = ps->o.blur_strength_iterations; i >= 1; i--) { + for (int i = iterations; i >= 1; i--) { const glx_blur_pass_t *up_pass = &ps->psglx->blur_passes[1]; bool is_last = (i == 1); assert(up_pass->prog); @@ -1742,7 +1755,7 @@ glx_kawase_blur_dst(session_t *ps, int dx, int dy, int width, int height, float glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); glUseProgram(up_pass->prog); if (up_pass->unifm_offset >= 0) - glUniform1f(up_pass->unifm_offset, ps->o.blur_strength_offset); + glUniform1f(up_pass->unifm_offset, offset); if (up_pass->unifm_halfpixel >= 0) glUniform2f(up_pass->unifm_halfpixel, 0.5 / tex_width, 0.5 / tex_height); if (up_pass->unifm_fulltex >= 0) From 241bbc50285e58cbc6a25d45066689eeea913880 Mon Sep 17 00:00:00 2001 From: Bernd Busse Date: Thu, 22 Feb 2018 21:55:28 +0100 Subject: [PATCH 8/8] update sample config --- compton.sample.conf | 2 ++ 1 file changed, 2 insertions(+) diff --git a/compton.sample.conf b/compton.sample.conf index a964dc47..e6eb37af 100644 --- a/compton.sample.conf +++ b/compton.sample.conf @@ -32,6 +32,8 @@ alpha-step = 0.06; # inactive-dim-fixed = true; # blur-background = true; # blur-background-frame = true; +blur-method = "kawase"; +# blur-strength = 15; blur-kern = "3x3box"; # blur-kern = "5,5,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1"; # blur-background-fixed = true;