Merge pull request #382 from tryone144/feature/dual_kawase
dual-kawase blur algorithm for new OpenGL backend
This commit is contained in:
@@ -166,7 +166,7 @@ OPTIONS
|
||||
*--detect-client-leader*::
|
||||
Use 'WM_CLIENT_LEADER' to group windows, and consider windows in the same group focused at the same time. 'WM_TRANSIENT_FOR' has higher priority if *--detect-transient* is enabled, too.
|
||||
|
||||
*--blur-method*, *--blur-size*, *--blur-deviation*::
|
||||
*--blur-method*, *--blur-size*, *--blur-deviation*, *--blur-strength*::
|
||||
Parameters for background blurring, see the *BLUR* section for more information.
|
||||
|
||||
*--blur-background*::
|
||||
@@ -397,8 +397,8 @@ Available options of the 'blur' section are: ::
|
||||
|
||||
*method*:::
|
||||
A string. Controls the blur method. Corresponds to the *--blur-method* command line option. Available choices are:
|
||||
'none' to disable blurring; 'gaussian' for gaussian blur; 'box' for box blur; 'kernel' for convolution blur with a custom kernel.
|
||||
Note: 'gaussian' and 'box' blur methods are only supported by the experimental backends.
|
||||
'none' to disable blurring; 'gaussian' for gaussian blur; 'box' for box blur; 'kernel' for convolution blur with a custom kernel; 'dual_kawase' for dual-filter kawase blur.
|
||||
Note: 'gaussian', 'box' and 'dual_kawase' blur methods are only supported by the experimental backends.
|
||||
(default: none)
|
||||
|
||||
*size*:::
|
||||
@@ -407,6 +407,9 @@ Available options of the 'blur' section are: ::
|
||||
*deviation*:::
|
||||
A floating point number. The standard deviation for the 'gaussian' blur method. Corresponds to the *--blur-deviation* command line option (default: 0.84089642).
|
||||
|
||||
*strength*:::
|
||||
An integer in the range 0-20. The strength of the 'dual_kawase' blur method. Corresponds to the *--blur-strength* command line option. If set to zero, the value requested by *--blur-size* is approximated (default: 5).
|
||||
|
||||
*kernel*:::
|
||||
A string. The kernel to use for the 'kernel' blur method, specified in the same format as the *--blur-kerns* option. Corresponds to the *--blur-kerns* command line option.
|
||||
|
||||
|
||||
@@ -143,6 +143,8 @@ focus-exclude = [ "class_g = 'Cairo-clock'" ];
|
||||
# blur-size = 12
|
||||
#
|
||||
# blur-deviation = false
|
||||
#
|
||||
# blur-strength = 5
|
||||
|
||||
# Blur background of semi-transparent / ARGB windows.
|
||||
# Bad in performance, with driver-dependent behavior.
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "compiler.h"
|
||||
#include "config.h"
|
||||
#include "driver.h"
|
||||
#include "kernel.h"
|
||||
#include "region.h"
|
||||
@@ -65,6 +65,11 @@ struct kernel_blur_args {
|
||||
int kernel_count;
|
||||
};
|
||||
|
||||
struct dual_kawase_blur_args {
|
||||
int size;
|
||||
int strength;
|
||||
};
|
||||
|
||||
struct backend_operations {
|
||||
// =========== Initialization ===========
|
||||
|
||||
|
||||
@@ -363,6 +363,69 @@ struct conv **generate_blur_kernel(enum blur_method method, void *args, int *ker
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/// Generate kernel parameters for dual-kawase blur method. Falls back on approximating
|
||||
/// standard gauss radius if strength is zero or below.
|
||||
struct dual_kawase_params *generate_dual_kawase_params(void *args) {
|
||||
struct dual_kawase_blur_args *blur_args = args;
|
||||
static const struct {
|
||||
int iterations; /// Number of down- and upsample iterations
|
||||
float offset; /// Sample offset in half-pixels
|
||||
int min_radius; /// Approximate gauss-blur with at least this
|
||||
/// radius and std-deviation
|
||||
} strength_levels[20] = {
|
||||
{.iterations = 1, .offset = 1.25f, .min_radius = 1}, // LVL 1
|
||||
{.iterations = 1, .offset = 2.25f, .min_radius = 6}, // LVL 2
|
||||
{.iterations = 2, .offset = 2.00f, .min_radius = 11}, // LVL 3
|
||||
{.iterations = 2, .offset = 3.00f, .min_radius = 17}, // LVL 4
|
||||
{.iterations = 2, .offset = 4.25f, .min_radius = 24}, // LVL 5
|
||||
{.iterations = 3, .offset = 2.50f, .min_radius = 32}, // LVL 6
|
||||
{.iterations = 3, .offset = 3.25f, .min_radius = 40}, // LVL 7
|
||||
{.iterations = 3, .offset = 4.25f, .min_radius = 51}, // LVL 8
|
||||
{.iterations = 3, .offset = 5.50f, .min_radius = 67}, // LVL 9
|
||||
{.iterations = 4, .offset = 3.25f, .min_radius = 83}, // LVL 10
|
||||
{.iterations = 4, .offset = 4.00f, .min_radius = 101}, // LVL 11
|
||||
{.iterations = 4, .offset = 5.00f, .min_radius = 123}, // LVL 12
|
||||
{.iterations = 4, .offset = 6.00f, .min_radius = 148}, // LVL 13
|
||||
{.iterations = 4, .offset = 7.25f, .min_radius = 178}, // LVL 14
|
||||
{.iterations = 4, .offset = 8.25f, .min_radius = 208}, // LVL 15
|
||||
{.iterations = 5, .offset = 4.50f, .min_radius = 236}, // LVL 16
|
||||
{.iterations = 5, .offset = 5.25f, .min_radius = 269}, // LVL 17
|
||||
{.iterations = 5, .offset = 6.25f, .min_radius = 309}, // LVL 18
|
||||
{.iterations = 5, .offset = 7.25f, .min_radius = 357}, // LVL 19
|
||||
{.iterations = 5, .offset = 8.50f, .min_radius = 417}, // LVL 20
|
||||
};
|
||||
|
||||
auto params = ccalloc(1, struct dual_kawase_params);
|
||||
params->iterations = 0;
|
||||
params->offset = 1.0f;
|
||||
|
||||
if (blur_args->strength <= 0 && blur_args->size) {
|
||||
// find highest level that approximates blur-strength with the selected
|
||||
// gaussian blur-radius
|
||||
int lvl = 1;
|
||||
while (strength_levels[lvl - 1].min_radius < blur_args->size && lvl < 20) {
|
||||
++lvl;
|
||||
}
|
||||
blur_args->strength = lvl;
|
||||
}
|
||||
if (blur_args->strength <= 0) {
|
||||
// default value
|
||||
blur_args->strength = 5;
|
||||
}
|
||||
|
||||
assert(blur_args->strength > 0 && blur_args->strength <= 20);
|
||||
params->iterations = strength_levels[blur_args->strength - 1].iterations;
|
||||
params->offset = strength_levels[blur_args->strength - 1].offset;
|
||||
|
||||
// Expand sample area to cover the smallest texture / highest selected iteration:
|
||||
// - Smallest texture dimensions are halved `iterations`-times
|
||||
// - Upsample needs pixels two-times `offset` away from the border
|
||||
// - Plus one for interpolation differences
|
||||
params->expand = (1 << params->iterations) * 2 * (int)ceil(params->offset) + 1;
|
||||
|
||||
return params;
|
||||
}
|
||||
|
||||
void init_backend_base(struct backend_base *base, session_t *ps) {
|
||||
base->c = ps->c;
|
||||
base->loop = ps->loop;
|
||||
|
||||
@@ -16,6 +16,15 @@ typedef struct conv conv;
|
||||
typedef struct backend_base backend_t;
|
||||
struct backend_operations;
|
||||
|
||||
struct dual_kawase_params {
|
||||
/// Number of downsample passes
|
||||
int iterations;
|
||||
/// Pixel offset for down- and upsample
|
||||
float offset;
|
||||
/// Save area around blur target (@ref resize_width, @ref resize_height)
|
||||
int expand;
|
||||
};
|
||||
|
||||
bool build_shadow(xcb_connection_t *, xcb_drawable_t, double opacity, int width,
|
||||
int height, const conv *kernel, xcb_render_picture_t shadow_pixel,
|
||||
xcb_pixmap_t *pixmap, xcb_render_picture_t *pict);
|
||||
@@ -41,3 +50,4 @@ default_backend_render_shadow(backend_t *backend_data, int width, int height,
|
||||
void init_backend_base(struct backend_base *base, session_t *ps);
|
||||
|
||||
struct conv **generate_blur_kernel(enum blur_method method, void *args, int *kernel_count);
|
||||
struct dual_kawase_params *generate_dual_kawase_params(void *args);
|
||||
|
||||
@@ -32,14 +32,24 @@ struct gl_blur_context {
|
||||
enum blur_method method;
|
||||
gl_blur_shader_t *blur_shader;
|
||||
|
||||
/// Temporary textures used for blurring. They are always the same size as the
|
||||
/// target, so they are always big enough without resizing.
|
||||
/// Turns out calling glTexImage to resize is expensive, so we avoid that.
|
||||
GLuint blur_texture[2];
|
||||
/// Temporary fbo used for blurring
|
||||
GLuint blur_fbo;
|
||||
/// Temporary textures used for blurring
|
||||
GLuint *blur_textures;
|
||||
int blur_texture_count;
|
||||
/// Temporary fbos used for blurring
|
||||
GLuint *blur_fbos;
|
||||
int blur_fbo_count;
|
||||
|
||||
int texture_width, texture_height;
|
||||
/// Cached dimensions of each blur_texture. They are the same size as the target,
|
||||
/// so they are always big enough without resizing.
|
||||
/// Turns out calling glTexImage to resize is expensive, so we avoid that.
|
||||
struct texture_size {
|
||||
int width;
|
||||
int height;
|
||||
} * texture_sizes;
|
||||
|
||||
/// Cached dimensions of the offscreen framebuffer. It's the same size as the
|
||||
/// target but is expanded in either direction by resize_width / resize_height.
|
||||
int fb_width, fb_height;
|
||||
|
||||
/// How much do we need to resize the damaged region for blurring.
|
||||
int resize_width, resize_height;
|
||||
@@ -536,24 +546,247 @@ void gl_compose(backend_t *base, void *image_data, int dst_x, int dst_y,
|
||||
/**
|
||||
* Blur contents in a particular region.
|
||||
*/
|
||||
bool gl_blur(backend_t *base, double opacity, void *ctx, const region_t *reg_blur,
|
||||
const region_t *reg_visible attr_unused) {
|
||||
struct gl_blur_context *bctx = ctx;
|
||||
bool gl_kernel_blur(backend_t *base, double opacity, void *ctx, const rect_t *extent,
|
||||
const int nrects, const GLuint vao[2]) {
|
||||
auto bctx = (struct gl_blur_context *)ctx;
|
||||
auto gd = (struct gl_data *)base;
|
||||
|
||||
if (gd->width + bctx->resize_width * 2 != bctx->texture_width ||
|
||||
gd->height + bctx->resize_height * 2 != bctx->texture_height) {
|
||||
int dst_y_screen_coord = gd->height - extent->y2,
|
||||
dst_y_fb_coord = bctx->fb_height - extent->y2;
|
||||
|
||||
int curr = 0;
|
||||
for (int i = 0; i < bctx->npasses; ++i) {
|
||||
const gl_blur_shader_t *p = &bctx->blur_shader[i];
|
||||
assert(p->prog);
|
||||
|
||||
assert(bctx->blur_textures[curr]);
|
||||
|
||||
// The origin to use when sampling from the source texture
|
||||
GLint texorig_x, texorig_y;
|
||||
GLint tex_width, tex_height;
|
||||
GLuint src_texture;
|
||||
|
||||
if (i == 0) {
|
||||
texorig_x = extent->x1;
|
||||
texorig_y = dst_y_screen_coord;
|
||||
src_texture = gd->back_texture;
|
||||
tex_width = gd->width;
|
||||
tex_height = gd->height;
|
||||
} else {
|
||||
texorig_x = extent->x1 + bctx->resize_width;
|
||||
texorig_y = dst_y_fb_coord - bctx->resize_height;
|
||||
src_texture = bctx->blur_textures[curr];
|
||||
auto src_size = bctx->texture_sizes[curr];
|
||||
tex_width = src_size.width;
|
||||
tex_height = src_size.height;
|
||||
}
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, src_texture);
|
||||
glUseProgram(p->prog);
|
||||
glUniform2f(p->unifm_pixel_norm, 1.0f / (GLfloat)tex_width,
|
||||
1.0f / (GLfloat)tex_height);
|
||||
if (i < bctx->npasses - 1) {
|
||||
assert(bctx->blur_fbos[0]);
|
||||
assert(bctx->blur_textures[!curr]);
|
||||
|
||||
// not last pass, draw into framebuffer, with resized regions
|
||||
glBindVertexArray(vao[1]);
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, bctx->blur_fbos[0]);
|
||||
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
|
||||
GL_TEXTURE_2D, bctx->blur_textures[!curr], 0);
|
||||
glDrawBuffer(GL_COLOR_ATTACHMENT0);
|
||||
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
|
||||
log_error("Framebuffer attachment failed.");
|
||||
return false;
|
||||
}
|
||||
|
||||
glUniform1f(p->unifm_opacity, 1.0);
|
||||
glUniform2f(p->orig_loc, (GLfloat)bctx->resize_width,
|
||||
-(GLfloat)bctx->resize_height);
|
||||
} else {
|
||||
// last pass, draw directly into the back buffer, with origin
|
||||
// regions
|
||||
glBindVertexArray(vao[0]);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, gd->back_fbo);
|
||||
|
||||
glUniform1f(p->unifm_opacity, (float)opacity);
|
||||
glUniform2f(p->orig_loc, 0, 0);
|
||||
}
|
||||
|
||||
glUniform2f(p->texorig_loc, (GLfloat)texorig_x, (GLfloat)texorig_y);
|
||||
glDrawElements(GL_TRIANGLES, nrects * 6, GL_UNSIGNED_INT, NULL);
|
||||
|
||||
// XXX use multiple draw calls is probably going to be slow than
|
||||
// just simply blur the whole area.
|
||||
|
||||
curr = !curr;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool gl_dual_kawase_blur(backend_t *base, double opacity, void *ctx, const rect_t *extent,
|
||||
const int nrects, const GLuint vao[2]) {
|
||||
auto bctx = (struct gl_blur_context *)ctx;
|
||||
auto gd = (struct gl_data *)base;
|
||||
|
||||
int dst_y_screen_coord = gd->height - extent->y2,
|
||||
dst_y_fb_coord = bctx->fb_height - extent->y2;
|
||||
|
||||
int iterations = bctx->blur_texture_count;
|
||||
int scale_factor = 1;
|
||||
|
||||
// Kawase downsample pass
|
||||
const gl_blur_shader_t *down_pass = &bctx->blur_shader[0];
|
||||
assert(down_pass->prog);
|
||||
glUseProgram(down_pass->prog);
|
||||
|
||||
// Downsample always renders with resize offset
|
||||
glUniform2f(down_pass->orig_loc, (GLfloat)bctx->resize_width,
|
||||
-(GLfloat)bctx->resize_height);
|
||||
|
||||
for (int i = 0; i < iterations; ++i) {
|
||||
// Scale output width / height by half in each iteration
|
||||
scale_factor <<= 1;
|
||||
|
||||
GLuint src_texture;
|
||||
int tex_width, tex_height;
|
||||
int texorig_x, texorig_y;
|
||||
|
||||
if (i == 0) {
|
||||
// first pass: copy from back buffer
|
||||
src_texture = gd->back_texture;
|
||||
tex_width = gd->width;
|
||||
tex_height = gd->height;
|
||||
|
||||
texorig_x = extent->x1;
|
||||
texorig_y = dst_y_screen_coord;
|
||||
} else {
|
||||
// copy from previous pass
|
||||
src_texture = bctx->blur_textures[i - 1];
|
||||
auto src_size = bctx->texture_sizes[i - 1];
|
||||
tex_width = src_size.width;
|
||||
tex_height = src_size.height;
|
||||
|
||||
texorig_x = extent->x1 + bctx->resize_width;
|
||||
texorig_y = dst_y_fb_coord - bctx->resize_height;
|
||||
}
|
||||
|
||||
assert(src_texture);
|
||||
assert(bctx->blur_fbos[i]);
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, src_texture);
|
||||
glBindVertexArray(vao[1]);
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, bctx->blur_fbos[i]);
|
||||
glDrawBuffer(GL_COLOR_ATTACHMENT0);
|
||||
|
||||
glUniform2f(down_pass->texorig_loc, (GLfloat)texorig_x, (GLfloat)texorig_y);
|
||||
glUniform1f(down_pass->scale_loc, (GLfloat)scale_factor);
|
||||
|
||||
glUniform2f(down_pass->unifm_pixel_norm, 1.0f / (GLfloat)tex_width,
|
||||
1.0f / (GLfloat)tex_height);
|
||||
|
||||
glDrawElements(GL_TRIANGLES, nrects * 6, GL_UNSIGNED_INT, NULL);
|
||||
}
|
||||
|
||||
// Kawase upsample pass
|
||||
const gl_blur_shader_t *up_pass = &bctx->blur_shader[1];
|
||||
assert(up_pass->prog);
|
||||
glUseProgram(up_pass->prog);
|
||||
|
||||
// Upsample always samples from textures with resize offset
|
||||
glUniform2f(up_pass->texorig_loc, (GLfloat)(extent->x1 + bctx->resize_width),
|
||||
(GLfloat)(dst_y_fb_coord - bctx->resize_height));
|
||||
|
||||
for (int i = iterations - 1; i >= 0; --i) {
|
||||
// Scale output width / height back by two in each iteration
|
||||
scale_factor >>= 1;
|
||||
|
||||
const GLuint src_texture = bctx->blur_textures[i];
|
||||
assert(src_texture);
|
||||
|
||||
// Calculate normalized half-width/-height of a src pixel
|
||||
auto src_size = bctx->texture_sizes[i];
|
||||
int tex_width = src_size.width;
|
||||
int tex_height = src_size.height;
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, src_texture);
|
||||
if (i > 0) {
|
||||
assert(bctx->blur_fbos[i - 1]);
|
||||
|
||||
// not last pass, draw into next framebuffer
|
||||
glBindVertexArray(vao[1]);
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, bctx->blur_fbos[i - 1]);
|
||||
glDrawBuffer(GL_COLOR_ATTACHMENT0);
|
||||
|
||||
glUniform2f(up_pass->orig_loc, (GLfloat)bctx->resize_width,
|
||||
-(GLfloat)bctx->resize_height);
|
||||
glUniform1f(up_pass->unifm_opacity, (GLfloat)1);
|
||||
} else {
|
||||
// last pass, draw directly into the back buffer
|
||||
glBindVertexArray(vao[0]);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, gd->back_fbo);
|
||||
|
||||
glUniform2f(up_pass->orig_loc, (GLfloat)0, (GLfloat)0);
|
||||
glUniform1f(up_pass->unifm_opacity, (GLfloat)opacity);
|
||||
}
|
||||
|
||||
glUniform1f(up_pass->scale_loc, (GLfloat)scale_factor);
|
||||
glUniform2f(up_pass->unifm_pixel_norm, 1.0f / (GLfloat)tex_width,
|
||||
1.0f / (GLfloat)tex_height);
|
||||
|
||||
glDrawElements(GL_TRIANGLES, nrects * 6, GL_UNSIGNED_INT, NULL);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool gl_blur(backend_t *base, double opacity, void *ctx, const region_t *reg_blur,
|
||||
const region_t *reg_visible attr_unused) {
|
||||
auto bctx = (struct gl_blur_context *)ctx;
|
||||
auto gd = (struct gl_data *)base;
|
||||
|
||||
bool ret = false;
|
||||
|
||||
if (gd->width + bctx->resize_width * 2 != bctx->fb_width ||
|
||||
gd->height + bctx->resize_height * 2 != bctx->fb_height) {
|
||||
// Resize the temporary textures used for blur in case the root
|
||||
// size changed
|
||||
bctx->texture_width = gd->width + bctx->resize_width * 2;
|
||||
bctx->texture_height = gd->height + bctx->resize_height * 2;
|
||||
bctx->fb_width = gd->width + bctx->resize_width * 2;
|
||||
bctx->fb_height = gd->height + bctx->resize_height * 2;
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, bctx->blur_texture[0]);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, bctx->texture_width,
|
||||
bctx->texture_height, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL);
|
||||
glBindTexture(GL_TEXTURE_2D, bctx->blur_texture[1]);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, bctx->texture_width,
|
||||
bctx->texture_height, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL);
|
||||
for (int i = 0; i < bctx->blur_texture_count; ++i) {
|
||||
auto tex_size = bctx->texture_sizes + i;
|
||||
if (bctx->method == BLUR_METHOD_DUAL_KAWASE) {
|
||||
// Use smaller textures for each iteration (quarter of the
|
||||
// previous texture)
|
||||
tex_size->width = 1 + ((bctx->fb_width - 1) >> (i + 1));
|
||||
tex_size->height = 1 + ((bctx->fb_height - 1) >> (i + 1));
|
||||
} else {
|
||||
tex_size->width = bctx->fb_width;
|
||||
tex_size->height = bctx->fb_height;
|
||||
}
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, bctx->blur_textures[i]);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, tex_size->width,
|
||||
tex_size->height, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL);
|
||||
|
||||
if (bctx->method == BLUR_METHOD_DUAL_KAWASE) {
|
||||
// Attach texture to FBO target
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, bctx->blur_fbos[i]);
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER,
|
||||
GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
|
||||
bctx->blur_textures[i], 0);
|
||||
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) !=
|
||||
GL_FRAMEBUFFER_COMPLETE) {
|
||||
log_error("Framebuffer attachment failed.");
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
||||
}
|
||||
|
||||
// Remainder: regions are in Xorg coordinates
|
||||
@@ -562,13 +795,10 @@ bool gl_blur(backend_t *base, double opacity, void *ctx, const region_t *reg_blu
|
||||
const rect_t *extent = pixman_region32_extents((region_t *)reg_blur),
|
||||
*extent_resized = pixman_region32_extents(®_blur_resized);
|
||||
int width = extent->x2 - extent->x1, height = extent->y2 - extent->y1;
|
||||
int dst_y_resized_screen_coord = gd->height - extent_resized->y2,
|
||||
dst_y_resized_fb_coord = bctx->texture_height - extent_resized->y2;
|
||||
if (width == 0 || height == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ret = false;
|
||||
int nrects, nrects_resized;
|
||||
const rect_t *rects = pixman_region32_rectangles((region_t *)reg_blur, &nrects),
|
||||
*rects_resized =
|
||||
@@ -580,13 +810,13 @@ bool gl_blur(backend_t *base, double opacity, void *ctx, const region_t *reg_blu
|
||||
auto coord = ccalloc(nrects * 16, GLint);
|
||||
auto indices = ccalloc(nrects * 6, GLuint);
|
||||
x_rect_to_coords(nrects, rects, extent_resized->x1, extent_resized->y2,
|
||||
bctx->texture_height, gd->height, false, coord, indices);
|
||||
bctx->fb_height, gd->height, false, coord, indices);
|
||||
|
||||
auto coord_resized = ccalloc(nrects_resized * 16, GLint);
|
||||
auto indices_resized = ccalloc(nrects_resized * 6, GLuint);
|
||||
x_rect_to_coords(nrects_resized, rects_resized, extent_resized->x1,
|
||||
extent_resized->y2, bctx->texture_height, bctx->texture_height,
|
||||
false, coord_resized, indices_resized);
|
||||
extent_resized->y2, bctx->fb_height, bctx->fb_height, false,
|
||||
coord_resized, indices_resized);
|
||||
pixman_region32_fini(®_blur_resized);
|
||||
|
||||
GLuint vao[2];
|
||||
@@ -620,74 +850,12 @@ bool gl_blur(backend_t *base, double opacity, void *ctx, const region_t *reg_blu
|
||||
glVertexAttribPointer(vert_in_texcoord_loc, 2, GL_INT, GL_FALSE,
|
||||
sizeof(GLint) * 4, (void *)(sizeof(GLint) * 2));
|
||||
|
||||
int curr = 0;
|
||||
for (int i = 0; i < bctx->npasses; ++i) {
|
||||
const gl_blur_shader_t *p = &bctx->blur_shader[i];
|
||||
assert(p->prog);
|
||||
|
||||
assert(bctx->blur_texture[curr]);
|
||||
|
||||
// The origin to use when sampling from the source texture
|
||||
GLint texorig_x, texorig_y;
|
||||
GLint tex_width, tex_height;
|
||||
GLuint src_texture;
|
||||
|
||||
if (i == 0) {
|
||||
texorig_x = extent_resized->x1;
|
||||
texorig_y = dst_y_resized_screen_coord;
|
||||
tex_width = gd->width;
|
||||
tex_height = gd->height;
|
||||
src_texture = gd->back_texture;
|
||||
} else {
|
||||
texorig_x = 0;
|
||||
texorig_y = 0;
|
||||
tex_width = bctx->texture_width;
|
||||
tex_height = bctx->texture_height;
|
||||
src_texture = bctx->blur_texture[curr];
|
||||
}
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, src_texture);
|
||||
glUseProgram(p->prog);
|
||||
glUniform2f(p->unifm_pixel_norm, 1.0f / (GLfloat)tex_width,
|
||||
1.0f / (GLfloat)tex_height);
|
||||
if (i < bctx->npasses - 1) {
|
||||
// not last pass, draw into framebuffer, with resized regions
|
||||
glBindVertexArray(vao[1]);
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, bctx->blur_fbo);
|
||||
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
|
||||
GL_TEXTURE_2D, bctx->blur_texture[!curr], 0);
|
||||
glDrawBuffer(GL_COLOR_ATTACHMENT0);
|
||||
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
|
||||
log_error("Framebuffer attachment failed.");
|
||||
goto end;
|
||||
}
|
||||
glUniform1f(p->unifm_opacity, 1.0);
|
||||
// For other than last pass, we are drawing to a texture, we
|
||||
// translate the render origin so we don't need a big texture
|
||||
glUniform2f(p->orig_loc, -(GLfloat)extent_resized->x1,
|
||||
-(GLfloat)dst_y_resized_fb_coord);
|
||||
} else {
|
||||
// last pass, draw directly into the back buffer, with origin
|
||||
// regions
|
||||
glBindVertexArray(vao[0]);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, gd->back_fbo);
|
||||
glUniform1f(p->unifm_opacity, (float)opacity);
|
||||
glUniform2f(p->orig_loc, 0, 0);
|
||||
}
|
||||
|
||||
glUniform2f(p->texorig_loc, (GLfloat)texorig_x, (GLfloat)texorig_y);
|
||||
glDrawElements(GL_TRIANGLES, nrects * 6, GL_UNSIGNED_INT, NULL);
|
||||
|
||||
// XXX use multiple draw calls is probably going to be slow than
|
||||
// just simply blur the whole area.
|
||||
|
||||
curr = !curr;
|
||||
if (bctx->method == BLUR_METHOD_DUAL_KAWASE) {
|
||||
ret = gl_dual_kawase_blur(base, opacity, ctx, extent_resized, nrects, vao);
|
||||
} else {
|
||||
ret = gl_kernel_blur(base, opacity, ctx, extent_resized, nrects, vao);
|
||||
}
|
||||
|
||||
ret = true;
|
||||
|
||||
end:
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
@@ -695,6 +863,7 @@ end:
|
||||
glDeleteBuffers(4, bo);
|
||||
glBindVertexArray(0);
|
||||
glDeleteVertexArrays(2, vao);
|
||||
glUseProgram(0);
|
||||
|
||||
free(indices);
|
||||
free(coord);
|
||||
@@ -702,20 +871,20 @@ end:
|
||||
free(coord_resized);
|
||||
|
||||
gl_check_err();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// clang-format off
|
||||
const char *vertex_shader = GLSL(330,
|
||||
uniform mat4 projection;
|
||||
uniform float scale = 1.0;
|
||||
uniform vec2 orig;
|
||||
uniform vec2 texorig;
|
||||
layout(location = 0) in vec2 coord;
|
||||
layout(location = 1) in vec2 in_texcoord;
|
||||
out vec2 texcoord;
|
||||
void main() {
|
||||
gl_Position = projection * vec4(coord + orig, 0, 1);
|
||||
gl_Position = projection * vec4(coord + orig, 0, scale);
|
||||
texcoord = in_texcoord + texorig;
|
||||
}
|
||||
);
|
||||
@@ -918,17 +1087,28 @@ static inline void gl_free_blur_shader(gl_blur_shader_t *shader) {
|
||||
}
|
||||
|
||||
void gl_destroy_blur_context(backend_t *base attr_unused, void *ctx) {
|
||||
struct gl_blur_context *bctx = ctx;
|
||||
auto bctx = (struct gl_blur_context *)ctx;
|
||||
// Free GLSL shaders/programs
|
||||
for (int i = 0; i < bctx->npasses; ++i) {
|
||||
gl_free_blur_shader(&bctx->blur_shader[i]);
|
||||
}
|
||||
free(bctx->blur_shader);
|
||||
|
||||
glDeleteTextures(bctx->npasses > 1 ? 2 : 1, bctx->blur_texture);
|
||||
if (bctx->npasses > 1) {
|
||||
glDeleteFramebuffers(1, &bctx->blur_fbo);
|
||||
if (bctx->blur_texture_count && bctx->blur_textures) {
|
||||
glDeleteTextures(bctx->blur_texture_count, bctx->blur_textures);
|
||||
free(bctx->blur_textures);
|
||||
}
|
||||
if (bctx->blur_texture_count && bctx->texture_sizes) {
|
||||
free(bctx->texture_sizes);
|
||||
}
|
||||
if (bctx->blur_fbo_count && bctx->blur_fbos) {
|
||||
glDeleteFramebuffers(bctx->blur_fbo_count, bctx->blur_fbos);
|
||||
free(bctx->blur_fbos);
|
||||
}
|
||||
|
||||
bctx->blur_texture_count = 0;
|
||||
bctx->blur_fbo_count = 0;
|
||||
|
||||
free(bctx);
|
||||
|
||||
gl_check_err();
|
||||
@@ -937,17 +1117,12 @@ void gl_destroy_blur_context(backend_t *base attr_unused, void *ctx) {
|
||||
/**
|
||||
* Initialize GL blur filters.
|
||||
*/
|
||||
void *gl_create_blur_context(backend_t *base, enum blur_method method, void *args) {
|
||||
bool success = true;
|
||||
auto gd = (struct gl_data *)base;
|
||||
bool gl_create_kernel_blur_context(void *blur_context, GLfloat *projection,
|
||||
enum blur_method method, void *args) {
|
||||
bool success = false;
|
||||
auto ctx = (struct gl_blur_context *)blur_context;
|
||||
|
||||
struct conv **kernels;
|
||||
auto ctx = ccalloc(1, struct gl_blur_context);
|
||||
|
||||
if (!method || method >= BLUR_METHOD_INVALID) {
|
||||
ctx->method = BLUR_METHOD_NONE;
|
||||
return ctx;
|
||||
}
|
||||
|
||||
int nkernels;
|
||||
ctx->method = BLUR_METHOD_KERNEL;
|
||||
@@ -960,18 +1135,12 @@ void *gl_create_blur_context(backend_t *base, enum blur_method method, void *arg
|
||||
|
||||
if (!nkernels) {
|
||||
ctx->method = BLUR_METHOD_NONE;
|
||||
return ctx;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Set projection matrix to gl viewport dimensions so we can use screen
|
||||
// coordinates for all vertices
|
||||
// Note: OpenGL matrices are column major
|
||||
GLint viewport_dimensions[2];
|
||||
glGetIntegerv(GL_MAX_VIEWPORT_DIMS, viewport_dimensions);
|
||||
GLfloat projection_matrix[4][4] = {{2.0f / (GLfloat)viewport_dimensions[0], 0, 0, 0},
|
||||
{0, 2.0f / (GLfloat)viewport_dimensions[1], 0, 0},
|
||||
{0, 0, 0, 0},
|
||||
{-1, -1, 0, 1}};
|
||||
// Specify required textures and FBOs
|
||||
ctx->blur_texture_count = 2;
|
||||
ctx->blur_fbo_count = 1;
|
||||
|
||||
ctx->blur_shader = ccalloc(max2(2, nkernels), gl_blur_shader_t);
|
||||
|
||||
@@ -1102,7 +1271,7 @@ void *gl_create_blur_context(backend_t *base, enum blur_method method, void *arg
|
||||
// Setup projection matrix
|
||||
glUseProgram(pass->prog);
|
||||
int pml = glGetUniformLocationChecked(pass->prog, "projection");
|
||||
glUniformMatrix4fv(pml, 1, false, projection_matrix[0]);
|
||||
glUniformMatrix4fv(pml, 1, false, projection);
|
||||
glUseProgram(0);
|
||||
|
||||
ctx->resize_width += kern->w / 2;
|
||||
@@ -1122,7 +1291,7 @@ void *gl_create_blur_context(backend_t *base, enum blur_method method, void *arg
|
||||
// Setup projection matrix
|
||||
glUseProgram(pass->prog);
|
||||
int pml = glGetUniformLocationChecked(pass->prog, "projection");
|
||||
glUniformMatrix4fv(pml, 1, false, projection_matrix[0]);
|
||||
glUniformMatrix4fv(pml, 1, false, projection);
|
||||
glUseProgram(0);
|
||||
|
||||
ctx->npasses = 2;
|
||||
@@ -1130,26 +1299,7 @@ void *gl_create_blur_context(backend_t *base, enum blur_method method, void *arg
|
||||
ctx->npasses = nkernels;
|
||||
}
|
||||
|
||||
// Texture size will be defined by gl_blur
|
||||
glGenTextures(2, ctx->blur_texture);
|
||||
glBindTexture(GL_TEXTURE_2D, ctx->blur_texture[0]);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
glBindTexture(GL_TEXTURE_2D, ctx->blur_texture[1]);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
// Generate FBO and textures when needed
|
||||
glGenFramebuffers(1, &ctx->blur_fbo);
|
||||
if (!ctx->blur_fbo) {
|
||||
log_error("Failed to generate framebuffer object for blur");
|
||||
success = false;
|
||||
goto out;
|
||||
}
|
||||
|
||||
success = true;
|
||||
out:
|
||||
if (method != BLUR_METHOD_KERNEL) {
|
||||
// We generated the blur kernels, so we need to free them
|
||||
@@ -1159,22 +1309,244 @@ out:
|
||||
free(kernels);
|
||||
}
|
||||
|
||||
if (!success) {
|
||||
gl_destroy_blur_context(&gd->base, ctx);
|
||||
ctx = NULL;
|
||||
}
|
||||
|
||||
free(extension);
|
||||
// Restore LC_NUMERIC
|
||||
setlocale(LC_NUMERIC, lc_numeric_old);
|
||||
free(lc_numeric_old);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
bool gl_create_dual_kawase_blur_context(void *blur_context, GLfloat *projection,
|
||||
enum blur_method method, void *args) {
|
||||
bool success = false;
|
||||
auto ctx = (struct gl_blur_context *)blur_context;
|
||||
|
||||
ctx->method = method;
|
||||
|
||||
auto blur_params = generate_dual_kawase_params(args);
|
||||
|
||||
// Specify required textures and FBOs
|
||||
ctx->blur_texture_count = blur_params->iterations;
|
||||
ctx->blur_fbo_count = blur_params->iterations;
|
||||
|
||||
ctx->resize_width += blur_params->expand;
|
||||
ctx->resize_height += blur_params->expand;
|
||||
|
||||
ctx->npasses = 2;
|
||||
ctx->blur_shader = ccalloc(ctx->npasses, gl_blur_shader_t);
|
||||
|
||||
char *lc_numeric_old = strdup(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");
|
||||
|
||||
// Dual-kawase downsample shader / program
|
||||
auto down_pass = ctx->blur_shader;
|
||||
{
|
||||
// clang-format off
|
||||
static const char *FRAG_SHADER_DOWN = GLSL(330,
|
||||
uniform sampler2D tex_src;
|
||||
uniform float scale = 1.0;
|
||||
uniform vec2 pixel_norm;
|
||||
in vec2 texcoord;
|
||||
out vec4 out_color;
|
||||
void main() {
|
||||
vec2 offset = %.7g * pixel_norm;
|
||||
vec2 uv = texcoord * pixel_norm * (2.0 / scale);
|
||||
vec4 sum = texture2D(tex_src, uv) * 4.0;
|
||||
sum += texture2D(tex_src, uv - vec2(0.5, 0.5) * offset);
|
||||
sum += texture2D(tex_src, uv + vec2(0.5, 0.5) * offset);
|
||||
sum += texture2D(tex_src, uv + vec2(0.5, -0.5) * offset);
|
||||
sum += texture2D(tex_src, uv - vec2(0.5, -0.5) * offset);
|
||||
out_color = sum / 8.0;
|
||||
}
|
||||
);
|
||||
// clang-format on
|
||||
|
||||
// Build shader
|
||||
size_t shader_len =
|
||||
strlen(FRAG_SHADER_DOWN) + 10 /* offset */ + 1 /* null terminator */;
|
||||
char *shader_str = ccalloc(shader_len, char);
|
||||
auto real_shader_len =
|
||||
snprintf(shader_str, shader_len, FRAG_SHADER_DOWN, blur_params->offset);
|
||||
CHECK(real_shader_len >= 0);
|
||||
CHECK((size_t)real_shader_len < shader_len);
|
||||
|
||||
// Build program
|
||||
down_pass->prog = gl_create_program_from_str(vertex_shader, shader_str);
|
||||
free(shader_str);
|
||||
if (!down_pass->prog) {
|
||||
log_error("Failed to create GLSL program.");
|
||||
success = false;
|
||||
goto out;
|
||||
}
|
||||
glBindFragDataLocation(down_pass->prog, 0, "out_color");
|
||||
|
||||
// Get uniform addresses
|
||||
down_pass->unifm_pixel_norm =
|
||||
glGetUniformLocationChecked(down_pass->prog, "pixel_norm");
|
||||
down_pass->orig_loc =
|
||||
glGetUniformLocationChecked(down_pass->prog, "orig");
|
||||
down_pass->texorig_loc =
|
||||
glGetUniformLocationChecked(down_pass->prog, "texorig");
|
||||
down_pass->scale_loc =
|
||||
glGetUniformLocationChecked(down_pass->prog, "scale");
|
||||
|
||||
// Setup projection matrix
|
||||
glUseProgram(down_pass->prog);
|
||||
int pml = glGetUniformLocationChecked(down_pass->prog, "projection");
|
||||
glUniformMatrix4fv(pml, 1, false, projection);
|
||||
glUseProgram(0);
|
||||
}
|
||||
|
||||
// Dual-kawase upsample shader / program
|
||||
auto up_pass = ctx->blur_shader + 1;
|
||||
{
|
||||
// clang-format off
|
||||
static const char *FRAG_SHADER_UP = GLSL(330,
|
||||
uniform sampler2D tex_src;
|
||||
uniform float scale = 1.0;
|
||||
uniform vec2 pixel_norm;
|
||||
uniform float opacity;
|
||||
in vec2 texcoord;
|
||||
out vec4 out_color;
|
||||
void main() {
|
||||
vec2 offset = %.7g * pixel_norm;
|
||||
vec2 uv = texcoord * pixel_norm / (2 * scale);
|
||||
vec4 sum = texture2D(tex_src, uv + vec2(-1.0, 0.0) * offset);
|
||||
sum += texture2D(tex_src, uv + vec2(-0.5, 0.5) * offset) * 2.0;
|
||||
sum += texture2D(tex_src, uv + vec2(0.0, 1.0) * offset);
|
||||
sum += texture2D(tex_src, uv + vec2(0.5, 0.5) * offset) * 2.0;
|
||||
sum += texture2D(tex_src, uv + vec2(1.0, 0.0) * offset);
|
||||
sum += texture2D(tex_src, uv + vec2(0.5, -0.5) * offset) * 2.0;
|
||||
sum += texture2D(tex_src, uv + vec2(0.0, -1.0) * offset);
|
||||
sum += texture2D(tex_src, uv + vec2(-0.5, -0.5) * offset) * 2.0;
|
||||
out_color = sum / 12.0 * opacity;
|
||||
}
|
||||
);
|
||||
// clang-format on
|
||||
|
||||
// Build shader
|
||||
size_t shader_len =
|
||||
strlen(FRAG_SHADER_UP) + 10 /* offset */ + 1 /* null terminator */;
|
||||
char *shader_str = ccalloc(shader_len, char);
|
||||
auto real_shader_len =
|
||||
snprintf(shader_str, shader_len, FRAG_SHADER_UP, blur_params->offset);
|
||||
CHECK(real_shader_len >= 0);
|
||||
CHECK((size_t)real_shader_len < shader_len);
|
||||
|
||||
// Build program
|
||||
up_pass->prog = gl_create_program_from_str(vertex_shader, shader_str);
|
||||
free(shader_str);
|
||||
if (!up_pass->prog) {
|
||||
log_error("Failed to create GLSL program.");
|
||||
success = false;
|
||||
goto out;
|
||||
}
|
||||
glBindFragDataLocation(up_pass->prog, 0, "out_color");
|
||||
|
||||
// Get uniform addresses
|
||||
up_pass->unifm_pixel_norm =
|
||||
glGetUniformLocationChecked(up_pass->prog, "pixel_norm");
|
||||
up_pass->unifm_opacity =
|
||||
glGetUniformLocationChecked(up_pass->prog, "opacity");
|
||||
up_pass->orig_loc = glGetUniformLocationChecked(up_pass->prog, "orig");
|
||||
up_pass->texorig_loc =
|
||||
glGetUniformLocationChecked(up_pass->prog, "texorig");
|
||||
up_pass->scale_loc = glGetUniformLocationChecked(up_pass->prog, "scale");
|
||||
|
||||
// Setup projection matrix
|
||||
glUseProgram(up_pass->prog);
|
||||
int pml = glGetUniformLocationChecked(up_pass->prog, "projection");
|
||||
glUniformMatrix4fv(pml, 1, false, projection);
|
||||
glUseProgram(0);
|
||||
}
|
||||
|
||||
success = true;
|
||||
out:
|
||||
free(blur_params);
|
||||
|
||||
if (!success) {
|
||||
ctx = NULL;
|
||||
}
|
||||
|
||||
// Restore LC_NUMERIC
|
||||
setlocale(LC_NUMERIC, lc_numeric_old);
|
||||
free(lc_numeric_old);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
void *gl_create_blur_context(backend_t *base, enum blur_method method, void *args) {
|
||||
bool success;
|
||||
auto gd = (struct gl_data *)base;
|
||||
|
||||
auto ctx = ccalloc(1, struct gl_blur_context);
|
||||
|
||||
if (!method || method >= BLUR_METHOD_INVALID) {
|
||||
ctx->method = BLUR_METHOD_NONE;
|
||||
return ctx;
|
||||
}
|
||||
|
||||
// Set projection matrix to gl viewport dimensions so we can use screen
|
||||
// coordinates for all vertices
|
||||
// Note: OpenGL matrices are column major
|
||||
GLint viewport_dimensions[2];
|
||||
glGetIntegerv(GL_MAX_VIEWPORT_DIMS, viewport_dimensions);
|
||||
GLfloat projection_matrix[4][4] = {{2.0f / (GLfloat)viewport_dimensions[0], 0, 0, 0},
|
||||
{0, 2.0f / (GLfloat)viewport_dimensions[1], 0, 0},
|
||||
{0, 0, 0, 0},
|
||||
{-1, -1, 0, 1}};
|
||||
|
||||
if (method == BLUR_METHOD_DUAL_KAWASE) {
|
||||
success = gl_create_dual_kawase_blur_context(ctx, projection_matrix[0],
|
||||
method, args);
|
||||
} else {
|
||||
success =
|
||||
gl_create_kernel_blur_context(ctx, projection_matrix[0], method, args);
|
||||
}
|
||||
if (!success || ctx->method == BLUR_METHOD_NONE) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
// Texture size will be defined by gl_blur
|
||||
ctx->blur_textures = ccalloc(ctx->blur_texture_count, GLuint);
|
||||
ctx->texture_sizes = ccalloc(ctx->blur_texture_count, struct texture_size);
|
||||
glGenTextures(ctx->blur_texture_count, ctx->blur_textures);
|
||||
|
||||
for (int i = 0; i < ctx->blur_texture_count; ++i) {
|
||||
glBindTexture(GL_TEXTURE_2D, ctx->blur_textures[i]);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
}
|
||||
|
||||
// Generate FBO and textures when needed
|
||||
ctx->blur_fbos = ccalloc(ctx->blur_fbo_count, GLuint);
|
||||
glGenFramebuffers(ctx->blur_fbo_count, ctx->blur_fbos);
|
||||
|
||||
for (int i = 0; i < ctx->blur_fbo_count; ++i) {
|
||||
if (!ctx->blur_fbos[i]) {
|
||||
log_error("Failed to generate framebuffer objects for blur");
|
||||
success = false;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
if (!success) {
|
||||
gl_destroy_blur_context(&gd->base, ctx);
|
||||
ctx = NULL;
|
||||
}
|
||||
|
||||
gl_check_err();
|
||||
return ctx;
|
||||
}
|
||||
|
||||
void gl_get_blur_size(void *blur_context, int *width, int *height) {
|
||||
struct gl_blur_context *ctx = blur_context;
|
||||
auto ctx = (struct gl_blur_context *)blur_context;
|
||||
*width = ctx->resize_width;
|
||||
*height = ctx->resize_height;
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@ typedef struct {
|
||||
GLint unifm_opacity;
|
||||
GLint orig_loc;
|
||||
GLint texorig_loc;
|
||||
GLint scale_loc;
|
||||
} gl_blur_shader_t;
|
||||
|
||||
typedef struct {
|
||||
|
||||
@@ -507,6 +507,12 @@ void *create_blur_context(backend_t *base attr_unused, enum blur_method method,
|
||||
ret->method = BLUR_METHOD_NONE;
|
||||
return ret;
|
||||
}
|
||||
if (method == BLUR_METHOD_DUAL_KAWASE) {
|
||||
log_warn("Blur method 'dual_kawase' is not compatible with the 'xrender' "
|
||||
"backend.");
|
||||
ret->method = BLUR_METHOD_NONE;
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret->method = BLUR_METHOD_KERNEL;
|
||||
struct conv **kernels;
|
||||
|
||||
@@ -88,6 +88,13 @@ enum blur_method parse_blur_method(const char *src) {
|
||||
return BLUR_METHOD_BOX;
|
||||
} else if (strcmp(src, "gaussian") == 0) {
|
||||
return BLUR_METHOD_GAUSSIAN;
|
||||
} else if (strcmp(src, "dual_kawase") == 0) {
|
||||
return BLUR_METHOD_DUAL_KAWASE;
|
||||
} else if (strcmp(src, "kawase") == 0) {
|
||||
log_warn("Blur method 'kawase' has been renamed to 'dual_kawase'. "
|
||||
"Interpreted as 'dual_kawase', but this will stop working "
|
||||
"soon.");
|
||||
return BLUR_METHOD_DUAL_KAWASE;
|
||||
} else if (strcmp(src, "none") == 0) {
|
||||
return BLUR_METHOD_NONE;
|
||||
}
|
||||
@@ -542,6 +549,7 @@ char *parse_config(options_t *opt, const char *config_file, bool *shadow_enable,
|
||||
.blur_method = BLUR_METHOD_NONE,
|
||||
.blur_radius = 3,
|
||||
.blur_deviation = 0.84089642,
|
||||
.blur_strength = 5,
|
||||
.blur_background_frame = false,
|
||||
.blur_background_fixed = false,
|
||||
.blur_background_blacklist = NULL,
|
||||
|
||||
@@ -23,8 +23,8 @@
|
||||
#include "kernel.h"
|
||||
#include "log.h"
|
||||
#include "region.h"
|
||||
#include "win_defs.h"
|
||||
#include "types.h"
|
||||
#include "win_defs.h"
|
||||
|
||||
typedef struct session session_t;
|
||||
|
||||
@@ -60,6 +60,7 @@ enum blur_method {
|
||||
BLUR_METHOD_KERNEL,
|
||||
BLUR_METHOD_BOX,
|
||||
BLUR_METHOD_GAUSSIAN,
|
||||
BLUR_METHOD_DUAL_KAWASE,
|
||||
BLUR_METHOD_INVALID,
|
||||
};
|
||||
|
||||
@@ -189,6 +190,8 @@ typedef struct options {
|
||||
int blur_radius;
|
||||
// Standard deviation for the gaussian blur
|
||||
double blur_deviation;
|
||||
// Strength of the dual_kawase blur
|
||||
int blur_strength;
|
||||
/// Whether to blur background when the window frame is not opaque.
|
||||
/// Implies blur_background.
|
||||
bool blur_background_frame;
|
||||
|
||||
@@ -530,6 +530,8 @@ char *parse_config_libconfig(options_t *opt, const char *config_file, bool *shad
|
||||
config_lookup_int(&cfg, "blur-size", &opt->blur_radius);
|
||||
// --blur-deviation
|
||||
config_lookup_float(&cfg, "blur-deviation", &opt->blur_deviation);
|
||||
// --blur-strength
|
||||
config_lookup_int(&cfg, "blur-strength", &opt->blur_strength);
|
||||
// --blur-background
|
||||
if (config_lookup_bool(&cfg, "blur-background", &ival) && ival) {
|
||||
if (opt->blur_method == BLUR_METHOD_NONE) {
|
||||
@@ -640,6 +642,7 @@ char *parse_config_libconfig(options_t *opt, const char *config_file, bool *shad
|
||||
}
|
||||
|
||||
config_setting_lookup_float(blur_cfg, "deviation", &opt->blur_deviation);
|
||||
config_setting_lookup_int(blur_cfg, "strength", &opt->blur_strength);
|
||||
}
|
||||
|
||||
// Wintype settings
|
||||
|
||||
@@ -212,6 +212,9 @@ static void usage(const char *argv0, int ret) {
|
||||
"--blur-deviation\n"
|
||||
" The standard deviation for the 'gaussian' blur method.\n"
|
||||
"\n"
|
||||
"--blur-strength\n"
|
||||
" The strength level of the 'dual_kawase' blur method.\n"
|
||||
"\n"
|
||||
"--blur-background\n"
|
||||
" Blur background of semi-transparent / ARGB windows. Bad in\n"
|
||||
" performance. The switch name may change without prior\n"
|
||||
@@ -435,7 +438,8 @@ static const struct option longopts[] = {
|
||||
{"blur-method", required_argument, NULL, 328},
|
||||
{"blur-size", required_argument, NULL, 329},
|
||||
{"blur-deviation", required_argument, NULL, 330},
|
||||
{"shadow-color", required_argument, NULL, 331},
|
||||
{"blur-strength", required_argument, NULL, 331},
|
||||
{"shadow-color", required_argument, NULL, 332},
|
||||
{"experimental-backends", no_argument, NULL, 733},
|
||||
{"monitor-repaint", no_argument, NULL, 800},
|
||||
{"diagnostics", no_argument, NULL, 801},
|
||||
@@ -605,7 +609,7 @@ bool get_cfg(options_t *opt, int argc, char *const *argv, bool shadow_enable,
|
||||
case 256:
|
||||
// --config
|
||||
break;
|
||||
case 331:;
|
||||
case 332:;
|
||||
// --shadow-color
|
||||
struct color rgb;
|
||||
rgb = hex_to_rgb(optarg);
|
||||
@@ -844,6 +848,10 @@ bool get_cfg(options_t *opt, int argc, char *const *argv, bool shadow_enable,
|
||||
// --blur-deviation
|
||||
opt->blur_deviation = atof(optarg);
|
||||
break;
|
||||
case 331:
|
||||
// --blur-strength
|
||||
opt->blur_strength = atoi(optarg);
|
||||
break;
|
||||
|
||||
P_CASEBOOL(733, experimental_backends);
|
||||
P_CASEBOOL(800, monitor_repaint);
|
||||
@@ -933,6 +941,20 @@ bool get_cfg(options_t *opt, int argc, char *const *argv, bool shadow_enable,
|
||||
CHECK(opt->blur_kernel_count);
|
||||
}
|
||||
|
||||
// Sanitize parameters for dual-filter kawase blur
|
||||
if (opt->blur_method == BLUR_METHOD_DUAL_KAWASE) {
|
||||
if (opt->blur_strength <= 0 && opt->blur_radius > 500) {
|
||||
log_warn("Blur radius >500 not supported by dual_kawase method, "
|
||||
"capping to 500.");
|
||||
opt->blur_radius = 500;
|
||||
}
|
||||
if (opt->blur_strength > 20) {
|
||||
log_warn("Blur strength >20 not supported by dual_kawase method, "
|
||||
"capping to 20.");
|
||||
opt->blur_strength = 20;
|
||||
}
|
||||
}
|
||||
|
||||
if (opt->resize_damage < 0) {
|
||||
log_warn("Negative --resize-damage will not work correctly.");
|
||||
}
|
||||
|
||||
@@ -431,6 +431,7 @@ static bool initialize_blur(session_t *ps) {
|
||||
struct kernel_blur_args kargs;
|
||||
struct gaussian_blur_args gargs;
|
||||
struct box_blur_args bargs;
|
||||
struct dual_kawase_blur_args dkargs;
|
||||
|
||||
void *args = NULL;
|
||||
switch (ps->o.blur_method) {
|
||||
@@ -448,6 +449,11 @@ static bool initialize_blur(session_t *ps) {
|
||||
gargs.deviation = ps->o.blur_deviation;
|
||||
args = (void *)&gargs;
|
||||
break;
|
||||
case BLUR_METHOD_DUAL_KAWASE:
|
||||
dkargs.size = ps->o.blur_radius;
|
||||
dkargs.strength = ps->o.blur_strength;
|
||||
args = (void *)&dkargs;
|
||||
break;
|
||||
default: return true;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user