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:
parent
76992fd26e
commit
6034c77bc2
10 changed files with 590 additions and 16 deletions
|
@ -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,
|
||||
®_paint, ®_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) {
|
|||
®_paint_in_bound, ®_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, ®_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, ®_shadow, ®_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, ®_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, ®_shadow, ®_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,
|
||||
®_paint_in_bound, ®_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, ®_visible_local, (double[]){w->opacity});
|
||||
}
|
||||
ps->backend_data->ops->compose(ps->backend_data, new_img, w->g.x,
|
||||
w->g.y, ®_paint_in_bound,
|
||||
®_visible);
|
||||
ps->backend_data->ops->compose(ps->backend_data, w, new_img,
|
||||
w->g.x, w->g.y,
|
||||
®_paint_in_bound, ®_visible);
|
||||
ps->backend_data->ops->release_image(ps->backend_data, new_img);
|
||||
pixman_region32_fini(®_visible_local);
|
||||
pixman_region32_fini(®_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,
|
||||
®_bound, ®_visible);
|
||||
}
|
||||
|
||||
pixman_region32_fini(®_bound);
|
||||
pixman_region32_fini(®_paint_in_bound);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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?
|
||||
};
|
||||
|
||||
|
|
|
@ -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, ®);
|
||||
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, ®);
|
||||
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, ®);
|
||||
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(®);
|
||||
}
|
||||
|
||||
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
15
src/picom.c
15
src/picom.c
|
@ -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.
|
||||
|
|
Loading…
Add table
Reference in a new issue