1
0
Fork 0
mirror of https://github.com/yshui/picom.git synced 2025-04-21 18:03:02 -04:00

experimental backend changes

This commit is contained in:
Yuxuan Shui 2020-12-17 17:44:07 +00:00
parent 76992fd26e
commit 6034c77bc2
No known key found for this signature in database
GPG key ID: D3A4405BE6CC17F4
10 changed files with 590 additions and 16 deletions

View file

@ -149,7 +149,7 @@ void paint_all_new(session_t *ps, struct managed_win *t, bool ignore_damage) {
}
if (ps->root_image) {
ps->backend_data->ops->compose(ps->backend_data, ps->root_image, 0, 0,
ps->backend_data->ops->compose(ps->backend_data, t, ps->root_image, 0, 0,
&reg_paint, &reg_visible);
} else {
ps->backend_data->ops->fill(ps->backend_data, (struct color){0, 0, 0, 1},
@ -188,6 +188,18 @@ void paint_all_new(session_t *ps, struct managed_win *t, bool ignore_damage) {
&reg_paint_in_bound, &reg_visible);
}
// Store the window background for rounded corners
// If rounded corners backup the region first
if (w->corner_radius > 0) {
const int16_t x = w->g.x;
const int16_t y = w->g.y;
const auto wid = to_u16_checked(w->widthb);
const auto hei = to_u16_checked(w->heightb);
ps->backend_data->ops->store_back_texture(
ps->backend_data, w, ps->backend_round_context, &reg_bound, x,
y, wid, hei);
}
// Blur window background
/* TODO(yshui) since the backend might change the content of the window
* (e.g. with shaders), we should consult the backend whether the window
@ -303,7 +315,7 @@ void paint_all_new(session_t *ps, struct managed_win *t, bool ignore_damage) {
assert(w->shadow_image);
if (w->opacity == 1) {
ps->backend_data->ops->compose(
ps->backend_data, w->shadow_image, w->g.x + w->shadow_dx,
ps->backend_data, w, w->shadow_image, w->g.x + w->shadow_dx,
w->g.y + w->shadow_dy, &reg_shadow, &reg_visible);
} else {
auto new_img = ps->backend_data->ops->copy(
@ -312,7 +324,7 @@ void paint_all_new(session_t *ps, struct managed_win *t, bool ignore_damage) {
ps->backend_data, IMAGE_OP_APPLY_ALPHA_ALL, new_img,
NULL, &reg_visible, (double[]){w->opacity});
ps->backend_data->ops->compose(
ps->backend_data, new_img, w->g.x + w->shadow_dx,
ps->backend_data, w, new_img, w->g.x + w->shadow_dx,
w->g.y + w->shadow_dy, &reg_shadow, &reg_visible);
ps->backend_data->ops->release_image(ps->backend_data, new_img);
}
@ -328,7 +340,7 @@ void paint_all_new(session_t *ps, struct managed_win *t, bool ignore_damage) {
// Draw window on target
if (!w->invert_color && !w->dim && w->frame_opacity == 1 && w->opacity == 1) {
ps->backend_data->ops->compose(ps->backend_data, w->win_image,
ps->backend_data->ops->compose(ps->backend_data, w, w->win_image,
w->g.x, w->g.y,
&reg_paint_in_bound, &reg_visible);
} else if (w->opacity * MAX_ALPHA >= 1) {
@ -388,13 +400,21 @@ void paint_all_new(session_t *ps, struct managed_win *t, bool ignore_damage) {
ps->backend_data, IMAGE_OP_APPLY_ALPHA_ALL, new_img,
NULL, &reg_visible_local, (double[]){w->opacity});
}
ps->backend_data->ops->compose(ps->backend_data, new_img, w->g.x,
w->g.y, &reg_paint_in_bound,
&reg_visible);
ps->backend_data->ops->compose(ps->backend_data, w, new_img,
w->g.x, w->g.y,
&reg_paint_in_bound, &reg_visible);
ps->backend_data->ops->release_image(ps->backend_data, new_img);
pixman_region32_fini(&reg_visible_local);
pixman_region32_fini(&reg_bound_local);
}
// Round the corners as last step after blur/shadow/dim/etc
if (w->corner_radius > 0.0) {
ps->backend_data->ops->round(
ps->backend_data, w, ps->backend_round_context, w->win_image,
&reg_bound, &reg_visible);
}
pixman_region32_fini(&reg_bound);
pixman_region32_fini(&reg_paint_in_bound);
}

View file

@ -28,6 +28,9 @@ typedef struct backend_base {
/// Whether the backend can accept new render request at the moment
bool busy;
// ...
// Session data
session_t *ps;
} backend_t;
typedef void (*backend_ready_callback_t)(void *);
@ -70,6 +73,11 @@ struct dual_kawase_blur_args {
int strength;
};
struct round_corners_args {
int corner_radius;
bool round_borders;
};
struct backend_operations {
// =========== Initialization ===========
@ -142,6 +150,11 @@ struct backend_operations {
const region_t *reg_blur, const region_t *reg_visible)
attr_nonnull(1, 3, 4, 5);
/// Round a given region of the rendering buffer.
bool (*round)(backend_t *backend_data, struct managed_win *w, void *round_ctx,
void *image_data, const region_t *reg_round,
const region_t *reg_visible) attr_nonnull(1, 2, 3, 5, 6);
/// Update part of the back buffer with the rendering buffer, then present the
/// back buffer onto the target window (if not back buffered, update part of the
/// target window directly).
@ -230,6 +243,16 @@ struct backend_operations {
/// Get how many pixels outside of the blur area is needed for blur
void (*get_blur_size)(void *blur_context, int *width, int *height);
/// Backup our current window background so we can use it for "erasing" corners
bool (*store_back_texture)(backend_t *base, struct managed_win *w, void *ctx_,
const region_t *reg_tgt, int x, int y, int width,
int height);
/// Create a rounded corners context
void *(*create_round_context)(backend_t *base, void *args);
/// Destroy a rounded corners context
void (*destroy_round_context)(backend_t *base, void *ctx);
// =========== Hooks ============
/// Let the backend hook into the event handling queue
/// Not implemented yet

View file

@ -432,4 +432,5 @@ void init_backend_base(struct backend_base *base, session_t *ps) {
base->root = ps->root;
base->busy = false;
base->ops = NULL;
base->ps = ps;
}

View file

@ -56,8 +56,9 @@ static void dummy_check_image(struct backend_base *base, const struct dummy_imag
assert(*tmp->refcount > 0);
}
void dummy_compose(struct backend_base *base, void *image, int dst_x attr_unused,
int dst_y attr_unused, const region_t *reg_paint attr_unused,
void dummy_compose(struct backend_base *base, struct managed_win *w attr_unused,
void *image, int dst_x attr_unused, int dst_y attr_unused,
const region_t *reg_paint attr_unused,
const region_t *reg_visible attr_unused) {
dummy_check_image(base, image);
}
@ -72,6 +73,13 @@ bool dummy_blur(struct backend_base *backend_data attr_unused, double opacity at
return true;
}
bool dummy_round(struct backend_base *backend_data attr_unused,
struct managed_win *w attr_unused, void *ctx_ attr_unused,
void *image_data attr_unused, const region_t *reg_round attr_unused,
const region_t *reg_visible attr_unused) {
return true;
}
void *dummy_bind_pixmap(struct backend_base *base, xcb_pixmap_t pixmap,
struct xvisual_info fmt, bool owned attr_unused) {
auto dummy = (struct dummy_data *)base;
@ -138,6 +146,15 @@ void *dummy_create_blur_context(struct backend_base *base attr_unused,
void dummy_destroy_blur_context(struct backend_base *base attr_unused, void *ctx attr_unused) {
}
void *
dummy_create_round_context(struct backend_base *base attr_unused, void *args attr_unused) {
static int dummy_context;
return &dummy_context;
}
void dummy_destroy_round_context(struct backend_base *base attr_unused, void *ctx attr_unused) {
}
void dummy_get_blur_size(void *ctx attr_unused, int *width, int *height) {
// These numbers are arbitrary, to make sure the reisze_region code path is
// covered.
@ -145,12 +162,21 @@ void dummy_get_blur_size(void *ctx attr_unused, int *width, int *height) {
*height = 5;
}
bool dummy_store_back_texture(backend_t *backend_data attr_unused,
struct managed_win *w attr_unused, void *ctx_ attr_unused,
const region_t *reg_tgt attr_unused, int x attr_unused,
int y attr_unused, int width attr_unused,
int height attr_unused) {
return true;
}
struct backend_operations dummy_ops = {
.init = dummy_init,
.deinit = dummy_deinit,
.compose = dummy_compose,
.fill = dummy_fill,
.blur = dummy_blur,
.round = dummy_round,
.bind_pixmap = dummy_bind_pixmap,
.render_shadow = default_backend_render_shadow,
.release_image = dummy_release_image,
@ -162,6 +188,8 @@ struct backend_operations dummy_ops = {
.copy = dummy_image_copy,
.create_blur_context = dummy_create_blur_context,
.destroy_blur_context = dummy_destroy_blur_context,
.create_round_context = dummy_create_round_context,
.destroy_round_context = dummy_destroy_round_context,
.get_blur_size = dummy_get_blur_size,
.store_back_texture = dummy_store_back_texture
};

View file

@ -18,6 +18,7 @@
#include "string_utils.h"
#include "types.h"
#include "utils.h"
#include "win.h"
#include "backend/backend_common.h"
#include "backend/gl/gl_common.h"
@ -57,6 +58,20 @@ struct gl_blur_context {
int npasses;
};
struct gl_round_context {
gl_round_shader_t *round_shader;
GLuint *bg_fbo;
GLuint *bg_tex;
/// Cached size of each blur_texture
struct tex_size {
int width;
int height;
} * tex_sizes;
int tex_count;
int fbo_count;
bool round_borders;
};
static GLint glGetUniformLocationChecked(GLuint p, const char *name) {
auto ret = glGetUniformLocation(p, name);
if (ret < 0) {
@ -889,6 +904,182 @@ bool gl_blur(backend_t *base, double opacity, void *ctx, const region_t *reg_blu
return ret;
}
bool gl_round(backend_t *backend_data attr_unused, struct managed_win *w, void *ctx_,
void *image_data, const region_t *reg_round attr_unused,
const region_t *reg_visible attr_unused) {
struct gl_round_context *cctx = ctx_;
auto gd = (struct gl_data *)backend_data;
auto img = (struct gl_image *)image_data;
// log_warn("r(%d) b(%d), wxy(%d %d) wwh(%d %d) img(%d %d)",
// w->corner_radius, w->g.border_width, w->g.x, w->g.y,
// w->widthb, w->heightb, img->inner->width, img->inner->height);
int nrects;
const rect_t *rects;
rects = pixman_region32_rectangles((region_t *)reg_round, &nrects);
if (!nrects) {
// Nothing to paint
return false;
}
GLuint target = gd->back_fbo;
int dst_x = w->g.x;
int dst_y = w->g.y;
auto coord = ccalloc(nrects * 16, GLint);
auto indices = ccalloc(nrects * 6, GLuint);
x_rect_to_coords(nrects, rects, dst_x, dst_y, img ? img->inner->height : w->heightb,
gd->height, img ? img->inner->y_inverted : true, coord, indices);
GLuint vao;
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
GLuint bo[2];
glGenBuffers(2, bo);
glBindBuffer(GL_ARRAY_BUFFER, bo[0]);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bo[1]);
glBufferData(GL_ARRAY_BUFFER, (long)sizeof(*coord) * nrects * 16, coord, GL_STATIC_DRAW);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, (long)sizeof(*indices) * nrects * 6,
indices, GL_STATIC_DRAW);
glEnableVertexAttribArray(vert_coord_loc);
glEnableVertexAttribArray(vert_in_texcoord_loc);
glVertexAttribPointer(vert_coord_loc, 2, GL_INT, GL_FALSE, sizeof(GLint) * 4, NULL);
glVertexAttribPointer(vert_in_texcoord_loc, 2, GL_INT, GL_FALSE,
sizeof(GLint) * 4, (void *)(sizeof(GLint) * 2));
// XXX: do we need projection matrix 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}};
// glDisable(GL_BLEND);
glEnable(GL_BLEND);
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
// Bind texture
glViewport(0, 0, gd->width, gd->height);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, cctx->bg_tex[0]);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, img ? img->inner->texture : gd->back_texture);
const gl_round_shader_t *ppass = &cctx->round_shader[0];
glUseProgram(ppass->prog);
if (ppass->projection_loc >= 0)
glUniformMatrix4fv(ppass->projection_loc, 1, false, projection_matrix[0]);
if (ppass->unifm_tex_bg >= 0)
glUniform1i(ppass->unifm_tex_bg, (GLint)1);
if (ppass->unifm_radius)
glUniform1f(ppass->unifm_radius, (float)w->corner_radius);
if (ppass->unifm_texcoord)
glUniform2f(ppass->unifm_texcoord, (float)w->g.x, (float)w->g.y);
if (ppass->unifm_texsize)
glUniform2f(ppass->unifm_texsize, (float)w->widthb, (float)w->heightb);
if (ppass->unifm_borderw)
glUniform1f(ppass->unifm_borderw, (w->round_borders) ? w->g.border_width : 0);
if (ppass->unifm_resolution)
glUniform2f(ppass->unifm_resolution, (float)gd->width, (float)gd->height);
// Draw
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, target);
glDrawElements(GL_TRIANGLES, nrects * 6, GL_UNSIGNED_INT, NULL);
glDisableVertexAttribArray(vert_coord_loc);
glDisableVertexAttribArray(vert_in_texcoord_loc);
glBindVertexArray(0);
glDeleteVertexArrays(1, &vao);
// Cleanup
glBindTexture(GL_TEXTURE_2D, 0);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
glDrawBuffer(GL_BACK);
glEnable(GL_BLEND);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glDeleteBuffers(2, bo);
glUseProgram(0);
gl_check_err();
free(indices);
free(coord);
return true;
}
// Assumes the two textures are the same dimensions
static bool copyFrameBufferTexture(int width, int height, GLuint fboIn, GLuint textureIn,
GLuint fboOut, GLuint textureOut) {
bool ret = false;
// Bind input FBO + texture to a color attachment
glBindFramebuffer(GL_READ_FRAMEBUFFER, fboIn);
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
textureIn, 0);
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
log_error("Source framebuffer attachment failed.");
goto out;
}
glReadBuffer(GL_COLOR_ATTACHMENT0);
// Bind destination FBO + texture to another color attachment
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fboOut);
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D,
textureOut, 0);
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
log_error("Destination framebuffer attachment failed.");
goto out;
}
glDrawBuffer(GL_COLOR_ATTACHMENT1);
// specify source, destination drawing (sub)rectangles.
glBlitFramebuffer(0, 0, width, height, 0, 0, width, height, GL_COLOR_BUFFER_BIT,
GL_NEAREST);
ret = true;
out:
// unbind the color attachments
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, 0, 0);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
gl_check_err();
return ret;
}
bool gl_store_back_texture(backend_t *backend_data attr_unused,
struct managed_win *w attr_unused, void *ctx_ attr_unused,
const region_t *reg_tgt attr_unused, int x attr_unused,
int y attr_unused, int width attr_unused, int height attr_unused) {
struct gl_round_context *cctx = ctx_;
auto gd = (struct gl_data *)backend_data;
// log_info("Copying xy(%d %d) wh(%d %d)", x, y, width, height);
{
// Prepare our backup texture
glBindTexture(GL_TEXTURE_2D, cctx->bg_tex[0]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, gd->width, gd->height, 0, GL_BGR,
GL_UNSIGNED_BYTE, NULL);
glBindTexture(GL_TEXTURE_2D, 0);
copyFrameBufferTexture(gd->width, gd->height, gd->back_fbo,
gd->back_texture, cctx->bg_fbo[0], cctx->bg_tex[0]);
}
return true;
}
// clang-format off
const char *vertex_shader = GLSL(330,
uniform mat4 projection;
@ -1132,6 +1323,36 @@ void gl_destroy_blur_context(backend_t *base attr_unused, void *ctx) {
gl_check_err();
}
void gl_destroy_round_context(struct backend_base *base attr_unused, void *ctx attr_unused) {
struct gl_round_context *cctx = ctx;
if (cctx->round_shader && cctx->round_shader->prog) {
glDeleteProgram(cctx->round_shader->prog);
cctx->round_shader->prog = 0;
free(cctx->round_shader);
}
if (cctx->bg_tex) {
glDeleteTextures(cctx->tex_count, cctx->bg_tex);
free(cctx->bg_tex);
}
if (cctx->bg_fbo) {
glDeleteFramebuffers(cctx->fbo_count, cctx->bg_fbo);
free(cctx->bg_fbo);
}
if (cctx->tex_sizes) {
free(cctx->tex_sizes);
}
cctx->tex_count = 0;
cctx->fbo_count = 0;
free(cctx);
gl_check_err();
}
/**
* Initialize GL blur filters.
*/
@ -1569,6 +1790,170 @@ void gl_get_blur_size(void *blur_context, int *width, int *height) {
*height = ctx->resize_height;
}
void *gl_create_round_context(struct backend_base *base attr_unused, void *args attr_unused) {
bool success;
auto gd = (struct gl_data *)base;
auto ctx = ccalloc(1, struct gl_round_context);
struct round_corners_args *round_params = (struct round_corners_args *)args;
ctx->round_borders = round_params->round_borders;
ctx->round_shader = ccalloc(1, gl_round_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 pass = ctx->round_shader;
{
// TEST passthrough shader
/*static const char frag_passthrough[] = GLSL(330,
uniform sampler2D tex;
in vec2 texcoord;
void main() {
//gl_FragColor = texture2D(tex, texcoord);
//gl_FragColor = vec4(texture2D(tex, vec2(texcoord.xy),
0).rgb, 1); gl_FragColor = texelFetch(tex, ivec2(texcoord.xy), 0);
//gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}
);*/
// dst0 shader from opengl.c
// clang-format off
static const char *FRAG_SHADER_ROUND_CORNERS = GLSL(330,
uniform sampler2D tex;
uniform sampler2D tex_bg;
uniform float u_radius;
uniform float u_borderw;
uniform vec2 u_texcoord;
uniform vec2 u_texsize;
uniform vec2 u_resolution;
in vec2 texcoord;
out vec4 out_color;
// https://www.shadertoy.com/view/ltS3zW
float RectSDF(vec2 p, vec2 b, float r) {
vec2 d = abs(p) - b + vec2(r);
return min(max(d.x, d.y), 0.0) + length(max(d, 0.0)) - r;
}
void main() {
vec2 coord = vec2(u_texcoord.x, u_resolution.y-u_texsize.y-u_texcoord.y);
vec4 u_v4WndBgColor = texelFetch(tex_bg, ivec2(gl_FragCoord.xy), 0);
vec4 u_v4BorderColor = texelFetch(tex, ivec2(0, 0), 0);
vec4 u_v4FillColor = vec4(0.0, 0.0, 0.0, 0.0); // Inside rect, transparent
vec4 v4FromColor = u_v4BorderColor; // Always the border color. If no border, this still should be set
vec4 v4ToColor = u_v4WndBgColor; // Outside corners color = background texture
float u_fRadiusPx = u_radius;
float u_fHalfBorderThickness = u_borderw / 2.0;
// misc tests, uncomment for diff rect colors
//u_v4FillColor = texture2D(tex, texcoord/u_texsize);
//u_v4FillColor = texelFetch(tex, ivec2(texcoord.xy), 0);
//u_v4FillColor = vec4(0.0, 1.0, 0.0, 0.0); // Inside rect color
//v4FromColor = u_v4BorderColor = vec4(1.0, 1.0, 0.0, 1.0);
//v4ToColor = vec4(0.0, 0.0, 1.0, 1.0); //Outside color
vec2 u_v2HalfShapeSizePx = u_texsize/2.0 - vec2(u_fHalfBorderThickness);
vec2 v_v2CenteredPos = (gl_FragCoord.xy - u_texsize.xy / 2.0 - coord);
float fDist = RectSDF(v_v2CenteredPos, u_v2HalfShapeSizePx, u_fRadiusPx - u_fHalfBorderThickness);
if (u_fHalfBorderThickness > 0.0) {
if (fDist < 0.0) {
v4ToColor = u_v4FillColor;
}
fDist = abs(fDist) - u_fHalfBorderThickness;
} else {
v4FromColor = u_v4FillColor;
}
float fBlendAmount = smoothstep(-1.0, 1.0, fDist);
// final color
vec4 c = mix(v4FromColor, v4ToColor, fBlendAmount);
// we don't use discard due to alleged worse perf
// instead we can use alpha blending
//if ( c == vec4(0.0,0.0,0.0,0.0) ) discard; else
out_color = c;
}
);
// clang-format on
// Build shader
const char *SHADER_STR = FRAG_SHADER_ROUND_CORNERS;
// const char* SHADER_STR = frag_passthrough;
size_t shader_len = strlen(SHADER_STR) + 1 /* null terminator */;
char *shader_str = ccalloc(shader_len, char);
auto real_shader_len = snprintf(shader_str, shader_len, "%s", SHADER_STR);
CHECK(real_shader_len >= 0);
CHECK((size_t)real_shader_len < shader_len);
// Build program
pass->prog = gl_create_program_from_str(vertex_shader, shader_str);
free(shader_str);
if (!pass->prog) {
log_error("Failed to create GLSL program.");
success = false;
goto out;
}
glBindFragDataLocation(pass->prog, 0, "out_color");
// Get uniform addresses
pass->projection_loc =
glGetUniformLocationChecked(pass->prog, "projection");
pass->unifm_tex_bg = glGetUniformLocationChecked(pass->prog, "tex_bg");
pass->unifm_radius = glGetUniformLocationChecked(pass->prog, "u_radius");
pass->unifm_texcoord =
glGetUniformLocationChecked(pass->prog, "u_texcoord");
pass->unifm_texsize =
glGetUniformLocationChecked(pass->prog, "u_texsize");
pass->unifm_borderw =
glGetUniformLocationChecked(pass->prog, "u_borderw");
pass->unifm_resolution =
glGetUniformLocationChecked(pass->prog, "u_resolution");
}
// Texture size will be defined by gl_round
ctx->tex_count = 1;
ctx->bg_tex = ccalloc(ctx->tex_count, GLuint);
ctx->tex_sizes = ccalloc(ctx->tex_count, struct tex_size);
glGenTextures(ctx->tex_count, ctx->bg_tex);
for (int i = 0; i < ctx->tex_count; ++i) {
glBindTexture(GL_TEXTURE_2D, ctx->bg_tex[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->fbo_count = 1;
ctx->bg_fbo = ccalloc(ctx->fbo_count, GLuint);
glGenFramebuffers(ctx->fbo_count, ctx->bg_fbo);
for (int i = 0; i < ctx->fbo_count; ++i) {
if (!ctx->bg_fbo[i]) {
log_error("Failed to generate framebuffer object for blur");
success = false;
goto out;
}
}
success = true;
out:
// Restore LC_NUMERIC
setlocale(LC_NUMERIC, lc_numeric_old);
free(lc_numeric_old);
if (!success) {
gl_destroy_round_context(&gd->base, ctx);
ctx = NULL;
}
gl_check_err();
return ctx;
}
// clang-format off
const char *win_shader_glsl = GLSL(330,
uniform float opacity;

View file

@ -39,6 +39,18 @@ typedef struct {
GLint scale_loc;
} gl_blur_shader_t;
typedef struct {
GLuint prog;
GLint projection_loc;
GLint unifm_radius;
GLint unifm_texcoord;
GLint unifm_texsize;
GLint unifm_borderw;
GLint unifm_resolution;
GLint unifm_tex_bg;
GLint unifm_tex_wnd;
} gl_round_shader_t;
typedef struct {
GLuint prog;
GLint color_loc;
@ -123,6 +135,13 @@ void *gl_create_blur_context(backend_t *base, enum blur_method, void *args);
void gl_destroy_blur_context(backend_t *base, void *ctx);
void gl_get_blur_size(void *blur_context, int *width, int *height);
bool gl_round(backend_t *backend_data, struct managed_win *w, void *ctx_,
void *image_data, const region_t *reg_round, const region_t *reg_visible);
void *gl_create_round_context(backend_t *base, void *args);
void gl_destroy_round_context(backend_t *base, void *ctx);
bool gl_store_back_texture(backend_t *backend_data, struct managed_win *w, void *ctx_,
const region_t *reg_tgt, int x, int y, int width, int height);
bool gl_is_image_transparent(backend_t *base, void *image_data);
void gl_fill(backend_t *base, struct color, const region_t *clip);

View file

@ -489,6 +489,7 @@ struct backend_operations glx_ops = {
.image_op = gl_image_op,
.copy = gl_copy,
.blur = gl_blur,
.round = gl_round,
.is_image_transparent = gl_is_image_transparent,
.present = glx_present,
.buffer_age = glx_buffer_age,
@ -496,7 +497,10 @@ struct backend_operations glx_ops = {
.fill = gl_fill,
.create_blur_context = gl_create_blur_context,
.destroy_blur_context = gl_destroy_blur_context,
.create_round_context = gl_create_round_context,
.destroy_round_context = gl_destroy_round_context,
.get_blur_size = gl_get_blur_size,
.store_back_texture = gl_store_back_texture,
.max_buffer_age = 5, // Why?
};

View file

@ -90,8 +90,11 @@ struct _xrender_image_data {
bool owned;
};
static void compose(backend_t *base, void *img_data, int dst_x, int dst_y,
const region_t *reg_paint, const region_t *reg_visible) {
uint32_t make_rounded_window_shape(xcb_render_trapezoid_t traps[], uint32_t max_ntraps,
int cr, int wid, int hei);
static void compose(backend_t *base, struct managed_win *w, void *img_data, int dst_x,
int dst_y, const region_t *reg_paint, const region_t *reg_visible) {
struct _xrender_data *xd = (void *)base;
struct _xrender_image_data *img = img_data;
uint8_t op = (img->has_alpha ? XCB_RENDER_PICT_OP_OVER : XCB_RENDER_PICT_OP_SRC);
@ -104,10 +107,55 @@ static void compose(backend_t *base, void *img_data, int dst_x, int dst_y,
// sure we get everything into the buffer
x_clear_picture_clip_region(base->c, img->pict);
x_set_picture_clip_region(base->c, xd->back[2], 0, 0, &reg);
xcb_render_composite(base->c, op, img->pict, alpha_pict, xd->back[2], 0, 0, 0, 0,
to_i16_checked(dst_x), to_i16_checked(dst_y),
to_u16_checked(img->ewidth), to_u16_checked(img->eheight));
// Are we rounding corners?
session_t *ps = base->ps;
int cr = (w ? w->corner_radius : 0);
if (cr == 0) {
x_set_picture_clip_region(base->c, xd->back[2], 0, 0, &reg);
xcb_render_composite(base->c, op, img->pict, alpha_pict, xd->back[2], 0,
0, 0, 0, to_i16_checked(dst_x),
to_i16_checked(dst_y), to_u16_checked(img->ewidth),
to_u16_checked(img->eheight));
} else {
// Rounded corners
const int fullwid = w ? w->widthb : 0;
const int fullhei = w ? w->heightb : 0;
// const int fullwid = img->width;
// const int fullhei = img->height;
// log_warn("f(%d, %d) imge(%d %d) imgf(%d %d) sdw(%d %d) dst(%d %d)",
// fullwid, fullhei, img->ewidth, img->eheight, img->width, img->height,
// w->shadow_width, w->shadow_height, dst_x, dst_y);
xcb_render_picture_t p_tmp = x_create_picture_with_standard(
ps->c, ps->root, fullwid, fullhei, XCB_PICT_STANDARD_ARGB_32, 0, 0);
xcb_render_color_t trans = {.red = 0, .blue = 0, .green = 0, .alpha = 0};
const xcb_rectangle_t rect = {.x = 0,
.y = 0,
.width = to_u16_checked(fullwid),
.height = to_u16_checked(fullhei)};
xcb_render_fill_rectangles(ps->c, XCB_RENDER_PICT_OP_SRC, p_tmp, trans, 1,
&rect);
uint32_t max_ntraps = to_u32_checked(cr);
xcb_render_trapezoid_t traps[4 * max_ntraps + 5];
uint32_t n =
make_rounded_window_shape(traps, max_ntraps, cr, fullwid, fullhei);
xcb_render_trapezoids(ps->c, XCB_RENDER_PICT_OP_OVER, alpha_pict, p_tmp,
x_get_pictfmt_for_standard(ps->c, XCB_PICT_STANDARD_A_8),
0, 0, n, traps);
x_set_picture_clip_region(base->c, xd->back[2], 0, 0, &reg);
xcb_render_composite(base->c, XCB_RENDER_PICT_OP_OVER, img->pict, p_tmp,
xd->back[2], 0, 0, 0, 0,
// 0, 0, to_i16_checked(x), to_i16_checked(y),
to_i16_checked(dst_x), to_i16_checked(dst_y),
to_u16_checked(img->ewidth),
to_u16_checked(img->eheight));
xcb_render_free_picture(ps->c, p_tmp);
}
pixman_region32_fini(&reg);
}
@ -249,6 +297,16 @@ static bool blur(backend_t *backend_data, double opacity, void *ctx_,
return true;
}
static bool
x_round(struct backend_base *backend_data attr_unused, struct managed_win *w attr_unused,
void *ctx_ attr_unused, void *image_data attr_unused,
const region_t *reg_blur attr_unused, const region_t *reg_visible attr_unused) {
// dummy implementation, we already perform the rounding in compose
// TODO: should move the compose code here and call it from here
return true;
}
static void *
bind_pixmap(backend_t *base, xcb_pixmap_t pixmap, struct xvisual_info fmt, bool owned) {
xcb_generic_error_t *e;
@ -563,6 +621,21 @@ void get_blur_size(void *blur_context, int *width, int *height) {
*height = ctx->resize_height;
}
bool store_back_texture(backend_t *backend_data attr_unused,
struct managed_win *w attr_unused, void *ctx_ attr_unused,
const region_t *reg_tgt attr_unused, int x attr_unused,
int y attr_unused, int width attr_unused, int height attr_unused) {
return true;
}
void *create_round_context(struct backend_base *base attr_unused, void *args attr_unused) {
static int dummy_context;
return &dummy_context;
}
void destroy_round_context(struct backend_base *base attr_unused, void *ctx attr_unused) {
}
backend_t *backend_xrender_init(session_t *ps) {
auto xd = ccalloc(1, struct _xrender_data);
init_backend_base(&xd->base, ps);
@ -653,6 +726,7 @@ struct backend_operations xrender_ops = {
.init = backend_xrender_init,
.deinit = deinit,
.blur = blur,
.round = x_round,
.present = present,
.compose = compose,
.fill = fill,
@ -669,7 +743,10 @@ struct backend_operations xrender_ops = {
.copy = copy,
.create_blur_context = create_blur_context,
.destroy_blur_context = destroy_blur_context,
.create_round_context = create_round_context,
.destroy_round_context = destroy_round_context,
.get_blur_size = get_blur_size,
.store_back_texture = store_back_texture
};
// vim: set noet sw=8 ts=8:

View file

@ -157,6 +157,8 @@ typedef struct session {
backend_t *backend_data;
/// backend blur context
void *backend_blur_context;
/// round corners context
void *backend_round_context;
/// graphic drivers used
enum driver drivers;
/// file watch handle

View file

@ -479,6 +479,15 @@ static bool initialize_blur(session_t *ps) {
return ps->backend_blur_context != NULL;
}
static bool initialize_round_corners(session_t *ps) {
struct round_corners_args cargs;
cargs.corner_radius = ps->o.corner_radius;
cargs.round_borders = ps->o.round_borders;
ps->backend_round_context =
ps->backend_data->ops->create_round_context(ps->backend_data, &cargs);
return ps->backend_round_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) {
@ -501,6 +510,12 @@ static bool initialize_backend(session_t *ps) {
return false;
}
if (!initialize_round_corners(ps)) {
log_fatal("Failed to prepare for rounded corners, will "
"ignore...");
ps->o.corner_radius = 0;
}
// window_stack shouldn't include window that's
// not in the hash table at this point. Since
// there cannot be any fading windows.