new backends: blur interface update
To prepare for different blur methods, the blur interface of backends has been splitted into two parts. Now to use blur, a blur context must be created first; then, the blur method should be called with the blur context created. Updated the existing backends to the new interface. Also implemented handling of the new blur options. Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
This commit is contained in:
parent
1eba43f888
commit
67f0ec773a
|
@ -174,6 +174,7 @@ void paint_all_new(session_t *ps, struct managed_win *t, bool ignore_damage) {
|
|||
// We need to blur the bounding shape of the window
|
||||
// (reg_paint = reg_bound \cap reg_damage)
|
||||
ps->backend_data->ops->blur(ps->backend_data, w->opacity,
|
||||
ps->backend_blur_context,
|
||||
®_paint, ®_visible);
|
||||
} else if (frame_transparent && ps->o.blur_background_frame) {
|
||||
// Window itself is solid, we only need to blur the frame
|
||||
|
@ -183,6 +184,7 @@ void paint_all_new(session_t *ps, struct managed_win *t, bool ignore_damage) {
|
|||
// make sure reg_blur \in reg_damage
|
||||
pixman_region32_intersect(®_blur, ®_blur, ®_damage);
|
||||
ps->backend_data->ops->blur(ps->backend_data, w->opacity,
|
||||
ps->backend_blur_context,
|
||||
®_blur, ®_visible);
|
||||
pixman_region32_fini(®_blur);
|
||||
}
|
||||
|
|
|
@ -130,8 +130,9 @@ struct backend_operations {
|
|||
void (*fill)(backend_t *backend_data, struct color, const region_t *clip);
|
||||
|
||||
/// Blur a given region of the target.
|
||||
bool (*blur)(backend_t *backend_data, double opacity, const region_t *reg_blur,
|
||||
const region_t *reg_visible) attr_nonnull(1, 3, 4);
|
||||
bool (*blur)(backend_t *backend_data, double opacity, void *blur_ctx,
|
||||
const region_t *reg_blur, const region_t *reg_visible)
|
||||
attr_nonnull(1, 3, 4, 5);
|
||||
|
||||
/// Present the back buffer onto the screen.
|
||||
///
|
||||
|
@ -209,6 +210,10 @@ struct backend_operations {
|
|||
/// returned image should not affect the original image
|
||||
void *(*copy)(backend_t *base, const void *image_data, const region_t *reg_visible);
|
||||
|
||||
/// Create a blur context that can be used to call `blur`
|
||||
void *(*create_blur_context)(backend_t *base, enum blur_method, void *args);
|
||||
void (*destroy_blur_context)(backend_t *base, void *ctx);
|
||||
|
||||
// =========== Hooks ============
|
||||
/// Let the backend hook into the event handling queue
|
||||
void (*set_ready_callback)(backend_t *, backend_ready_callback_t cb);
|
||||
|
|
|
@ -17,12 +17,38 @@
|
|||
#include "string_utils.h"
|
||||
#include "utils.h"
|
||||
|
||||
#include "backend/gl/gl_common.h"
|
||||
#include "backend/backend_common.h"
|
||||
#include "backend/gl/gl_common.h"
|
||||
|
||||
#define GLSL(version, ...) "#version " #version "\n" #__VA_ARGS__
|
||||
#define QUOTE(...) #__VA_ARGS__
|
||||
|
||||
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;
|
||||
|
||||
int texture_width, texture_height;
|
||||
|
||||
int npasses;
|
||||
};
|
||||
|
||||
static GLint glGetUniformLocationChecked(GLuint p, const char *name) {
|
||||
auto ret = glGetUniformLocation(p, name);
|
||||
if (ret < 0) {
|
||||
log_error("Failed to get location of uniform '%s'. compton might not "
|
||||
"work correctly.",
|
||||
name);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
GLuint gl_create_shader(GLenum shader_type, const char *shader_str) {
|
||||
log_trace("===\n%s\n===", shader_str);
|
||||
|
||||
|
@ -345,10 +371,44 @@ 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, const region_t *reg_blur,
|
||||
bool gl_blur(backend_t *base, double opacity, void *ctx, const region_t *reg_blur,
|
||||
const region_t *reg_visible) {
|
||||
// Remainder: regions are in Xorg coordinates
|
||||
struct gl_blur_context *bctx = ctx;
|
||||
struct gl_data *gd = (void *)base;
|
||||
|
||||
if (gd->width != bctx->texture_width || gd->height != bctx->texture_height) {
|
||||
// Resize the temporary textures used for blur in case the root
|
||||
// size changed
|
||||
glBindTexture(GL_TEXTURE_2D, bctx->blur_texture[0]);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, gd->width, gd->height, 0,
|
||||
GL_BGRA, GL_UNSIGNED_BYTE, NULL);
|
||||
if (bctx->npasses > 1) {
|
||||
glBindTexture(GL_TEXTURE_2D, bctx->blur_texture[1]);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, gd->width, gd->height, 0,
|
||||
GL_BGRA, GL_UNSIGNED_BYTE, NULL);
|
||||
}
|
||||
|
||||
bctx->texture_width = gd->width;
|
||||
bctx->texture_height = gd->height;
|
||||
|
||||
// XXX: do we need projection matrix for blur at all?
|
||||
// Note: OpenGL matrices are column major
|
||||
GLfloat projection_matrix[4][4] = {{2.0f / (GLfloat)gd->width, 0, 0, 0},
|
||||
{0, 2.0f / (GLfloat)gd->height, 0, 0},
|
||||
{0, 0, 0, 0},
|
||||
{-1, -1, 0, 1}};
|
||||
|
||||
// Update projection matrices in the blur shaders
|
||||
for (int i = 0; i < bctx->npasses; i++) {
|
||||
assert(bctx->blur_shader[i].prog);
|
||||
glUseProgram(bctx->blur_shader[i].prog);
|
||||
int pml = glGetUniformLocationChecked(bctx->blur_shader[i].prog,
|
||||
"projection");
|
||||
glUniformMatrix4fv(pml, 1, false, projection_matrix[0]);
|
||||
}
|
||||
}
|
||||
|
||||
// Remainder: regions are in Xorg coordinates
|
||||
const rect_t *extent = pixman_region32_extents((region_t *)reg_blur);
|
||||
int width = extent->x2 - extent->x1, height = extent->y2 - extent->y1;
|
||||
int dst_y = gd->height - extent->y2;
|
||||
|
@ -356,9 +416,7 @@ bool gl_blur(backend_t *base, double opacity, const region_t *reg_blur,
|
|||
return true;
|
||||
}
|
||||
|
||||
// these should be arguments
|
||||
bool ret = false;
|
||||
|
||||
int nrects;
|
||||
const rect_t *rects = pixman_region32_rectangles((region_t *)reg_blur, &nrects);
|
||||
if (!nrects) {
|
||||
|
@ -384,24 +442,24 @@ bool gl_blur(backend_t *base, double opacity, const region_t *reg_blur,
|
|||
|
||||
int curr = 0;
|
||||
glReadBuffer(GL_BACK);
|
||||
glBindTexture(GL_TEXTURE_2D, gd->blur_texture[0]);
|
||||
glBindTexture(GL_TEXTURE_2D, bctx->blur_texture[0]);
|
||||
// Copy the area to be blurred into tmp buffer
|
||||
glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, extent->x1, dst_y, width, height);
|
||||
|
||||
for (int i = 0; i < gd->npasses; ++i) {
|
||||
const gl_blur_shader_t *p = &gd->blur_shader[i];
|
||||
for (int i = 0; i < bctx->npasses; ++i) {
|
||||
const gl_blur_shader_t *p = &bctx->blur_shader[i];
|
||||
assert(p->prog);
|
||||
|
||||
assert(gd->blur_texture[curr]);
|
||||
glBindTexture(GL_TEXTURE_2D, gd->blur_texture[curr]);
|
||||
assert(bctx->blur_texture[curr]);
|
||||
glBindTexture(GL_TEXTURE_2D, bctx->blur_texture[curr]);
|
||||
|
||||
glUseProgram(p->prog);
|
||||
if (i < gd->npasses - 1) {
|
||||
if (i < bctx->npasses - 1) {
|
||||
// not last pass, draw into framebuffer
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, gd->blur_fbo);
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, bctx->blur_fbo);
|
||||
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
|
||||
GL_TEXTURE_2D, gd->blur_texture[!curr], 0);
|
||||
GL_TEXTURE_2D, bctx->blur_texture[!curr], 0);
|
||||
glDrawBuffer(GL_COLOR_ATTACHMENT0);
|
||||
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
|
||||
log_error("Framebuffer attachment failed.");
|
||||
|
@ -414,7 +472,7 @@ bool gl_blur(backend_t *base, double opacity, const region_t *reg_blur,
|
|||
glDrawBuffer(GL_BACK);
|
||||
glUniform1f(p->unifm_opacity, (float)opacity);
|
||||
}
|
||||
if (i == gd->npasses - 1) {
|
||||
if (i == bctx->npasses - 1) {
|
||||
glUniform2f(p->orig_loc, 0, 0);
|
||||
} else {
|
||||
// For other than last pass, we are drawing to a texture, we
|
||||
|
@ -460,16 +518,6 @@ end:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static GLint glGetUniformLocationChecked(GLuint p, const char *name) {
|
||||
auto ret = glGetUniformLocation(p, name);
|
||||
if (ret < 0) {
|
||||
log_error("Failed to get location of uniform '%s'. compton might not "
|
||||
"work correctly.",
|
||||
name);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// clang-format off
|
||||
const char *vertex_shader = GLSL(330,
|
||||
uniform mat4 projection;
|
||||
|
@ -520,32 +568,13 @@ void gl_resize(struct gl_data *gd, int width, int height) {
|
|||
gd->height = height;
|
||||
gd->width = width;
|
||||
|
||||
// XXX: do we need projection matrix at all?
|
||||
// Note: OpenGL matrices are column major
|
||||
GLfloat projection_matrix[4][4] = {{2.0f / (GLfloat)width, 0, 0, 0},
|
||||
{0, 2.0f / (GLfloat)height, 0, 0},
|
||||
{0, 0, 0, 0},
|
||||
{-1, -1, 0, 1}};
|
||||
|
||||
if (gd->npasses > 0) {
|
||||
// Resize the temporary textures used for blur
|
||||
glBindTexture(GL_TEXTURE_2D, gd->blur_texture[0]);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, gd->width, gd->height, 0,
|
||||
GL_BGRA, GL_UNSIGNED_BYTE, NULL);
|
||||
if (gd->npasses > 1) {
|
||||
glBindTexture(GL_TEXTURE_2D, gd->blur_texture[1]);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, gd->width, gd->height, 0,
|
||||
GL_BGRA, GL_UNSIGNED_BYTE, NULL);
|
||||
}
|
||||
|
||||
// Update projection matrices in the blur shaders
|
||||
for (int i = 0; i < gd->npasses; i++) {
|
||||
assert(gd->blur_shader[i].prog);
|
||||
glUseProgram(gd->blur_shader[i].prog);
|
||||
int pml = glGetUniformLocationChecked(gd->blur_shader[i].prog,
|
||||
"projection");
|
||||
glUniformMatrix4fv(pml, 1, false, projection_matrix[0]);
|
||||
}
|
||||
}
|
||||
// Update projection matrix in the win shader
|
||||
glUseProgram(gd->win_shader.prog);
|
||||
int pml = glGetUniformLocationChecked(gd->win_shader.prog, "projection");
|
||||
|
@ -660,16 +689,60 @@ void *gl_copy(backend_t *base, const void *image_data, const region_t *reg_visib
|
|||
return new_img;
|
||||
}
|
||||
|
||||
static inline void gl_free_blur_shader(gl_blur_shader_t *shader) {
|
||||
if (shader->prog) {
|
||||
glDeleteProgram(shader->prog);
|
||||
}
|
||||
|
||||
shader->prog = 0;
|
||||
}
|
||||
|
||||
void gl_destroy_blur_context(backend_t *base, void *ctx) {
|
||||
struct gl_blur_context *bctx = 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);
|
||||
}
|
||||
free(bctx);
|
||||
|
||||
gl_check_err();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize GL blur filters.
|
||||
*/
|
||||
static bool gl_init_blur(struct gl_data *gd, conv *const *const kernels, int nkernels) {
|
||||
if (!nkernels) {
|
||||
return true;
|
||||
void *gl_create_blur_context(backend_t *base, enum blur_method method, void *args) {
|
||||
bool success = true;
|
||||
auto gd = (struct gl_data *)base;
|
||||
|
||||
struct conv **kernels;
|
||||
auto ctx = cmalloc(struct gl_blur_context);
|
||||
|
||||
if (!method || method >= BLUR_METHOD_INVALID) {
|
||||
ctx->method = BLUR_METHOD_NONE;
|
||||
return ctx;
|
||||
}
|
||||
|
||||
gd->npasses = nkernels;
|
||||
gd->blur_shader = ccalloc(gd->npasses, gl_blur_shader_t);
|
||||
ctx->method = BLUR_METHOD_KERNEL;
|
||||
if (method == BLUR_METHOD_KERNEL) {
|
||||
ctx->npasses = ((struct kernel_blur_args *)args)->kernel_count;
|
||||
kernels = ((struct kernel_blur_args *)args)->kernels;
|
||||
} else {
|
||||
kernels = generate_blur_kernel(method, args, &ctx->npasses);
|
||||
}
|
||||
|
||||
if (!ctx->npasses) {
|
||||
ctx->method = BLUR_METHOD_NONE;
|
||||
return ctx;
|
||||
}
|
||||
|
||||
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
|
||||
|
@ -701,7 +774,7 @@ static bool gl_init_blur(struct gl_data *gd, conv *const *const kernels, int nke
|
|||
const char *shader_add = FRAG_SHADER_BLUR_ADD;
|
||||
char *extension = strdup("");
|
||||
|
||||
for (int i = 0; i < nkernels; i++) {
|
||||
for (int i = 0; i < ctx->npasses; i++) {
|
||||
auto kern = kernels[i];
|
||||
// Build shader
|
||||
int width = kern->w, height = kern->h;
|
||||
|
@ -726,7 +799,7 @@ static bool gl_init_blur(struct gl_data *gd, conv *const *const kernels, int nke
|
|||
}
|
||||
}
|
||||
|
||||
auto pass = gd->blur_shader + i;
|
||||
auto pass = ctx->blur_shader + i;
|
||||
size_t shader_len = strlen(FRAG_SHADER_BLUR) + strlen(extension) +
|
||||
strlen(shader_body) + 10 /* sum */ +
|
||||
1 /* null terminator */;
|
||||
|
@ -742,7 +815,8 @@ static bool gl_init_blur(struct gl_data *gd, conv *const *const kernels, int nke
|
|||
free(shader_str);
|
||||
if (!pass->prog) {
|
||||
log_error("Failed to create GLSL program.");
|
||||
goto err;
|
||||
success = false;
|
||||
goto out;
|
||||
}
|
||||
glBindFragDataLocation(pass->prog, 0, "out_color");
|
||||
|
||||
|
@ -756,37 +830,46 @@ static bool gl_init_blur(struct gl_data *gd, conv *const *const kernels, int nke
|
|||
pass->in_texcoord = glGetAttribLocation(pass->prog, "in_texcoord");
|
||||
pass->coord_loc = glGetAttribLocation(pass->prog, "coord");
|
||||
}
|
||||
free(extension);
|
||||
|
||||
// Texture size will be defined by gl_resize
|
||||
glGenTextures(gd->npasses > 1 ? 2 : 1, gd->blur_texture);
|
||||
glBindTexture(GL_TEXTURE_2D, gd->blur_texture[0]);
|
||||
glGenTextures(ctx->npasses > 1 ? 2 : 1, ctx->blur_texture);
|
||||
glBindTexture(GL_TEXTURE_2D, ctx->blur_texture[0]);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
if (gd->npasses > 1) {
|
||||
glBindTexture(GL_TEXTURE_2D, gd->blur_texture[1]);
|
||||
if (ctx->npasses > 1) {
|
||||
glBindTexture(GL_TEXTURE_2D, ctx->blur_texture[1]);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
// Generate FBO and textures when needed
|
||||
glGenFramebuffers(1, &gd->blur_fbo);
|
||||
if (!gd->blur_fbo) {
|
||||
glGenFramebuffers(1, &ctx->blur_fbo);
|
||||
if (!ctx->blur_fbo) {
|
||||
log_error("Failed to generate framebuffer object for blur");
|
||||
return false;
|
||||
success = false;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
if (method != BLUR_METHOD_KERNEL) {
|
||||
// We generated the blur kernels, so we need to free them
|
||||
for (int i = 0; i < ctx->npasses; i++) {
|
||||
free(kernels[i]);
|
||||
}
|
||||
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);
|
||||
|
||||
gl_check_err();
|
||||
|
||||
return true;
|
||||
err:
|
||||
free(extension);
|
||||
setlocale(LC_NUMERIC, lc_numeric_old);
|
||||
free(lc_numeric_old);
|
||||
return false;
|
||||
return ctx;
|
||||
}
|
||||
|
||||
// clang-format off
|
||||
|
@ -828,9 +911,6 @@ bool gl_init(struct gl_data *gd, session_t *ps) {
|
|||
glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
|
||||
|
||||
gl_win_shader_from_string(vertex_shader, win_shader_glsl, &gd->win_shader);
|
||||
if (!gl_init_blur(gd, ps->o.blur_kerns, ps->o.blur_kernel_count)) {
|
||||
return false;
|
||||
}
|
||||
gd->fill_shader.prog = gl_create_program_from_str(fill_vert, fill_frag);
|
||||
gd->fill_shader.in_coord_loc =
|
||||
glGetAttribLocation(gd->fill_shader.prog, "in_coord");
|
||||
|
@ -857,28 +937,9 @@ bool gl_init(struct gl_data *gd, session_t *ps) {
|
|||
return true;
|
||||
}
|
||||
|
||||
static inline void gl_free_blur_shader(gl_blur_shader_t *shader) {
|
||||
if (shader->prog) {
|
||||
glDeleteProgram(shader->prog);
|
||||
}
|
||||
|
||||
shader->prog = 0;
|
||||
}
|
||||
|
||||
void gl_deinit(struct gl_data *gd) {
|
||||
// Free GLSL shaders/programs
|
||||
for (int i = 0; i < gd->npasses; ++i) {
|
||||
gl_free_blur_shader(&gd->blur_shader[i]);
|
||||
}
|
||||
free(gd->blur_shader);
|
||||
|
||||
gl_free_prog_main(&gd->win_shader);
|
||||
|
||||
glDeleteTextures(gd->npasses > 1 ? 2 : 1, gd->blur_texture);
|
||||
if (gd->npasses > 1) {
|
||||
glDeleteFramebuffers(1, &gd->blur_fbo);
|
||||
}
|
||||
|
||||
if (gd->logger) {
|
||||
log_remove_target_tls(gd->logger);
|
||||
gd->logger = NULL;
|
||||
|
|
|
@ -66,17 +66,8 @@ struct gl_data {
|
|||
bool is_nvidia;
|
||||
// Height and width of the viewport
|
||||
int height, width;
|
||||
int npasses;
|
||||
gl_win_shader_t win_shader;
|
||||
gl_fill_shader_t fill_shader;
|
||||
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;
|
||||
|
||||
/// Called when an gl_texture is decoupled from the texture it refers. Returns
|
||||
/// the decoupled user_data
|
||||
|
@ -88,17 +79,6 @@ struct gl_data {
|
|||
struct log_target *logger;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
/// Framebuffer used for blurring.
|
||||
GLuint fbo;
|
||||
/// Textures used for blurring.
|
||||
GLuint textures[2];
|
||||
/// Width of the textures.
|
||||
int width;
|
||||
/// Height of the textures.
|
||||
int height;
|
||||
} gl_blur_cache_t;
|
||||
|
||||
typedef struct session session_t;
|
||||
|
||||
#define GL_PROG_MAIN_INIT \
|
||||
|
@ -128,8 +108,10 @@ void gl_release_image(backend_t *base, void *image_data);
|
|||
|
||||
void *gl_copy(backend_t *base, const void *image_data, const region_t *reg_visible);
|
||||
|
||||
bool gl_blur(backend_t *base, double opacity, const region_t *reg_blur,
|
||||
bool gl_blur(backend_t *base, double opacity, void *, const region_t *reg_blur,
|
||||
const region_t *reg_visible);
|
||||
void *gl_create_blur_context(backend_t *base, enum blur_method, void *args);
|
||||
void gl_destroy_blur_context(backend_t *base, void *ctx);
|
||||
|
||||
bool gl_is_image_transparent(backend_t *base, void *image_data);
|
||||
void gl_fill(backend_t *base, struct color, const region_t *clip);
|
||||
|
|
|
@ -472,6 +472,8 @@ struct backend_operations glx_ops = {
|
|||
.buffer_age = glx_buffer_age,
|
||||
.render_shadow = default_backend_render_shadow,
|
||||
.fill = gl_fill,
|
||||
.create_blur_context = gl_create_blur_context,
|
||||
.destroy_blur_context = gl_destroy_blur_context,
|
||||
.max_buffer_age = 5, // Why?
|
||||
};
|
||||
|
||||
|
|
|
@ -59,14 +59,17 @@ typedef struct _xrender_data {
|
|||
/// Width and height of the target pixmap
|
||||
int target_width, target_height;
|
||||
|
||||
xcb_special_event_t *present_event;
|
||||
} xrender_data;
|
||||
|
||||
struct _xrender_blur_context {
|
||||
enum blur_method method;
|
||||
/// Blur kernels converted to X format
|
||||
struct x_convolution_kernel **x_blur_kernel;
|
||||
|
||||
/// Number of blur kernels
|
||||
int x_blur_kernel_count;
|
||||
|
||||
xcb_special_event_t *present_event;
|
||||
} xrender_data;
|
||||
};
|
||||
|
||||
struct _xrender_image_data {
|
||||
// Pixmap that the client window draws to,
|
||||
|
@ -123,8 +126,13 @@ static void fill(backend_t *base, struct color c, const region_t *clip) {
|
|||
.height = to_u16_checked(extent->y2 - extent->y1)}});
|
||||
}
|
||||
|
||||
static bool blur(backend_t *backend_data, double opacity, const region_t *reg_blur,
|
||||
const region_t *reg_visible) {
|
||||
static bool blur(backend_t *backend_data, double opacity, void *ctx_,
|
||||
const region_t *reg_blur, const region_t *reg_visible) {
|
||||
struct _xrender_blur_context *bctx = ctx_;
|
||||
if (bctx->method == BLUR_METHOD_NONE) {
|
||||
return true;
|
||||
}
|
||||
|
||||
struct _xrender_data *xd = (void *)backend_data;
|
||||
xcb_connection_t *c = xd->base.c;
|
||||
region_t reg_op;
|
||||
|
@ -178,17 +186,17 @@ static bool blur(backend_t *backend_data, double opacity, const region_t *reg_bl
|
|||
// For 1 pass, we do
|
||||
// back -(pass 1)-> tmp0 -(copy)-> target_buffer
|
||||
int i;
|
||||
for (i = 0; i < xd->x_blur_kernel_count; i++) {
|
||||
for (i = 0; i < bctx->x_blur_kernel_count; i++) {
|
||||
// Copy from source picture to destination. The filter must
|
||||
// be applied on source picture, to get the nearby pixels outside the
|
||||
// window.
|
||||
// TODO cache converted blur_kerns
|
||||
xcb_render_set_picture_filter(c, src_pict, to_u16_checked(strlen(filter)),
|
||||
filter,
|
||||
to_u32_checked(xd->x_blur_kernel[i]->size),
|
||||
xd->x_blur_kernel[i]->kernel);
|
||||
to_u32_checked(bctx->x_blur_kernel[i]->size),
|
||||
bctx->x_blur_kernel[i]->kernel);
|
||||
|
||||
if (i < xd->x_blur_kernel_count - 1 || i == 0) {
|
||||
if (i < bctx->x_blur_kernel_count - 1 || i == 0) {
|
||||
// This is not the last pass, or this is the first pass
|
||||
xcb_render_composite(c, XCB_RENDER_PICT_OP_SRC, src_pict,
|
||||
XCB_NONE, dst_pict, src_x, src_y, 0, 0, 0, 0,
|
||||
|
@ -273,10 +281,6 @@ static void deinit(backend_t *backend_data) {
|
|||
xcb_render_free_picture(xd->base.c, xd->back[i]);
|
||||
xcb_free_pixmap(xd->base.c, xd->back_pixmap[i]);
|
||||
}
|
||||
for (int i = 0; i < xd->x_blur_kernel_count; i++) {
|
||||
free(xd->x_blur_kernel[i]);
|
||||
}
|
||||
free(xd->x_blur_kernel);
|
||||
if (xd->present_event) {
|
||||
xcb_unregister_for_special_event(xd->base.c, xd->present_event);
|
||||
}
|
||||
|
@ -468,6 +472,50 @@ static void *copy(backend_t *base, const void *image, const region_t *reg) {
|
|||
return new_img;
|
||||
}
|
||||
|
||||
void *create_blur_context(backend_t *base, enum blur_method method, void *args) {
|
||||
auto ret = cmalloc(struct _xrender_blur_context);
|
||||
if (!method || method >= BLUR_METHOD_INVALID) {
|
||||
ret->method = BLUR_METHOD_NONE;
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret->method = BLUR_METHOD_KERNEL;
|
||||
struct conv **kernels;
|
||||
int kernel_count;
|
||||
if (method == BLUR_METHOD_KERNEL) {
|
||||
kernels = ((struct kernel_blur_args *)args)->kernels;
|
||||
kernel_count = ((struct kernel_blur_args *)args)->kernel_count;
|
||||
} else {
|
||||
kernels = generate_blur_kernel(method, args, &kernel_count);
|
||||
}
|
||||
|
||||
ret->x_blur_kernel = ccalloc(kernel_count, struct x_convolution_kernel *);
|
||||
for (int i = 0; i < kernel_count; i++) {
|
||||
int center = kernels[i]->h * kernels[i]->w / 2;
|
||||
x_create_convolution_kernel(kernels[i], kernels[i]->data[center],
|
||||
&ret->x_blur_kernel[i]);
|
||||
}
|
||||
ret->x_blur_kernel_count = kernel_count;
|
||||
|
||||
if (method != BLUR_METHOD_KERNEL) {
|
||||
// Kernels generated by generate_blur_kernel, so we need to free them.
|
||||
for (int i = 0; i < kernel_count; i++) {
|
||||
free(kernels[i]);
|
||||
}
|
||||
free(kernels);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void destroy_blur_context(backend_t *base, void *ctx_) {
|
||||
struct _xrender_blur_context *ctx = ctx_;
|
||||
for (int i = 0; i < ctx->x_blur_kernel_count; i++) {
|
||||
free(ctx->x_blur_kernel[i]);
|
||||
}
|
||||
free(ctx->x_blur_kernel);
|
||||
free(ctx);
|
||||
}
|
||||
|
||||
backend_t *backend_xrender_init(session_t *ps) {
|
||||
auto xd = ccalloc(1, struct _xrender_data);
|
||||
init_backend_base(&xd->base, ps);
|
||||
|
@ -550,15 +598,6 @@ backend_t *backend_xrender_init(session_t *ps) {
|
|||
xd->root_pict = x_create_picture_with_visual_and_pixmap(
|
||||
ps->c, ps->vis, root_pixmap, 0, NULL);
|
||||
}
|
||||
|
||||
xd->x_blur_kernel = ccalloc(ps->o.blur_kernel_count, struct x_convolution_kernel *);
|
||||
for (int i = 0; i < ps->o.blur_kernel_count; i++) {
|
||||
int center = ps->o.blur_kerns[i]->h * ps->o.blur_kerns[i]->w / 2;
|
||||
x_create_convolution_kernel(ps->o.blur_kerns[i],
|
||||
ps->o.blur_kerns[i]->data[center],
|
||||
&xd->x_blur_kernel[i]);
|
||||
}
|
||||
xd->x_blur_kernel_count = ps->o.blur_kernel_count;
|
||||
return &xd->base;
|
||||
err:
|
||||
deinit(&xd->base);
|
||||
|
@ -583,6 +622,8 @@ struct backend_operations xrender_ops = {
|
|||
|
||||
.image_op = image_op,
|
||||
.copy = copy,
|
||||
.create_blur_context = create_blur_context,
|
||||
.destroy_blur_context = destroy_blur_context,
|
||||
};
|
||||
|
||||
// vim: set noet sw=8 ts=8:
|
||||
|
|
|
@ -143,6 +143,9 @@ typedef struct session {
|
|||
ev_signal int_signal;
|
||||
/// backend data
|
||||
backend_t *backend_data;
|
||||
/// backend blur context
|
||||
void *backend_blur_context;
|
||||
/// graphic drivers used
|
||||
enum driver drivers;
|
||||
/// libev mainloop
|
||||
struct ev_loop *loop;
|
||||
|
|
|
@ -29,9 +29,9 @@
|
|||
#include <test.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "config.h"
|
||||
#include "compiler.h"
|
||||
#include "compton.h"
|
||||
#include "config.h"
|
||||
#include "err.h"
|
||||
#include "kernel.h"
|
||||
#ifdef CONFIG_OPENGL
|
||||
|
@ -666,11 +666,43 @@ static void destroy_backend(session_t *ps) {
|
|||
|
||||
if (ps->backend_data) {
|
||||
// deinit backend
|
||||
ps->backend_data->ops->destroy_blur_context(ps->backend_data,
|
||||
ps->backend_blur_context);
|
||||
ps->backend_blur_context = NULL;
|
||||
ps->backend_data->ops->deinit(ps->backend_data);
|
||||
ps->backend_data = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static bool initialize_blur(session_t *ps) {
|
||||
struct kernel_blur_args kargs;
|
||||
struct gaussian_blur_args gargs;
|
||||
struct box_blur_args bargs;
|
||||
|
||||
void *args = NULL;
|
||||
switch (ps->o.blur_method) {
|
||||
case BLUR_METHOD_BOX:
|
||||
bargs.size = ps->o.blur_radius;
|
||||
args = (void *)&bargs;
|
||||
break;
|
||||
case BLUR_METHOD_KERNEL:
|
||||
kargs.kernel_count = ps->o.blur_kernel_count;
|
||||
kargs.kernels = ps->o.blur_kerns;
|
||||
args = (void *)&kargs;
|
||||
break;
|
||||
case BLUR_METHOD_GAUSSIAN:
|
||||
gargs.size = ps->o.blur_radius;
|
||||
gargs.deviation = ps->o.blur_deviation;
|
||||
args = (void *)&gargs;
|
||||
break;
|
||||
default: return true;
|
||||
}
|
||||
|
||||
ps->backend_blur_context = ps->backend_data->ops->create_blur_context(
|
||||
ps->backend_data, ps->o.blur_method, args);
|
||||
return ps->backend_blur_context != NULL;
|
||||
}
|
||||
|
||||
/// Init the backend and bind all the window pixmap to backend images
|
||||
static bool initialize_backend(session_t *ps) {
|
||||
if (ps->o.experimental_backends) {
|
||||
|
@ -679,14 +711,20 @@ static bool initialize_backend(session_t *ps) {
|
|||
ps->backend_data = backend_list[ps->o.backend]->init(ps);
|
||||
if (!ps->backend_data) {
|
||||
log_fatal("Failed to initialize backend, aborting...");
|
||||
ps->quit = true;
|
||||
ev_break(ps->loop, EVBREAK_ALL);
|
||||
quit_compton(ps);
|
||||
return false;
|
||||
}
|
||||
ps->backend_data->ops = backend_list[ps->o.backend];
|
||||
|
||||
// window_stack shouldn't include window that's not in the hash table at
|
||||
// this point. Since there cannot be any fading windows.
|
||||
if (!initialize_blur(ps)) {
|
||||
log_fatal("Failed to prepare for background blur, aborting...");
|
||||
quit_compton(ps);
|
||||
return false;
|
||||
}
|
||||
|
||||
// window_stack shouldn't include window that's
|
||||
// not in the hash table at this point. Since
|
||||
// there cannot be any fading windows.
|
||||
HASH_ITER2(ps->windows, _w) {
|
||||
if (!_w->managed) {
|
||||
continue;
|
||||
|
@ -874,8 +912,7 @@ static bool register_cm(session_t *ps) {
|
|||
{
|
||||
auto pid = getpid();
|
||||
xcb_change_property(ps->c, XCB_PROP_MODE_REPLACE, ps->reg_win,
|
||||
ps->atoms->a_NET_WM_PID, XCB_ATOM_CARDINAL,
|
||||
32, 1, &pid);
|
||||
ps->atoms->a_NET_WM_PID, XCB_ATOM_CARDINAL, 32, 1, &pid);
|
||||
}
|
||||
|
||||
// Set COMPTON_VERSION
|
||||
|
|
Loading…
Reference in New Issue