diff --git a/src/backend/backend.c b/src/backend/backend.c index af98791..054f192 100644 --- a/src/backend/backend.c +++ b/src/backend/backend.c @@ -71,6 +71,32 @@ void paint_all_new(session_t *ps, struct managed_win *t, bool ignore_damage) { static struct timespec last_paint = {0}; #endif + region_t reg_paint; + assert(ps->o.blur_method != BLUR_METHOD_INVALID); + if (ps->o.blur_method != BLUR_METHOD_NONE && ps->backend_data->ops->get_blur_size) { + int blur_width, blur_height; + ps->backend_data->ops->get_blur_size(ps->backend_blur_context, + &blur_width, &blur_height); + if (t) { + // The region of screen a given window influences will be smeared + // out by blur. With more windows on top of the given window, the + // influences region will be smeared out more. + // + // Instead of accurately calculate how much bigger the damage + // region will be because of blur, we assume the worst case here. + // That is, the damaged window is at the bottom of the stack, and + // all other windows have semi-transparent background + resize_region_in_place(®_damage, blur_width * t->stacking_rank, + blur_height * t->stacking_rank); + pixman_region32_intersect(®_damage, ®_damage, &ps->screen_reg); + } + reg_paint = resize_region(®_damage, blur_width, blur_height); + pixman_region32_intersect(®_paint, ®_paint, &ps->screen_reg); + } else { + pixman_region32_init(®_paint); + pixman_region32_copy(®_paint, ®_damage); + } + // A hint to backend, the region that will be visible on screen // backend can optimize based on this info region_t reg_visible; @@ -85,12 +111,12 @@ void paint_all_new(session_t *ps, struct managed_win *t, bool ignore_damage) { // TODO Bind root pixmap if (ps->backend_data->ops->prepare) { - ps->backend_data->ops->prepare(ps->backend_data, ®_damage); + ps->backend_data->ops->prepare(ps->backend_data, ®_paint); } if (ps->root_image) { ps->backend_data->ops->compose(ps->backend_data, ps->root_image, 0, 0, - ®_damage, ®_visible); + ®_paint, ®_visible); } // Windows are sorted from bottom to top @@ -109,9 +135,9 @@ void paint_all_new(session_t *ps, struct managed_win *t, bool ignore_damage) { // Draw shadow on target if (w->shadow) { // Clip region for the shadow - // reg_shadow \in reg_damage + // reg_shadow \in reg_paint auto reg_shadow = win_extents_by_val(w); - pixman_region32_intersect(®_shadow, ®_shadow, ®_damage); + pixman_region32_intersect(®_shadow, ®_shadow, ®_paint); if (!ps->o.wintype_option[w->window_type].full_shadow) { pixman_region32_subtract(®_shadow, ®_shadow, ®_bound); } @@ -156,10 +182,10 @@ void paint_all_new(session_t *ps, struct managed_win *t, bool ignore_damage) { } // The clip region for the current window, in global/target coordinates - // reg_paint \in reg_damage - region_t reg_paint; - pixman_region32_init(®_paint); - pixman_region32_intersect(®_paint, ®_bound, ®_damage); + // reg_paint_in_bound \in reg_paint + region_t reg_paint_in_bound; + pixman_region32_init(®_paint_in_bound); + pixman_region32_intersect(®_paint_in_bound, ®_bound, ®_paint); // Blur window background // TODO since the background might change the content of the window (e.g. @@ -176,10 +202,10 @@ void paint_all_new(session_t *ps, struct managed_win *t, bool ignore_damage) { // TODO resize blur region to fix black line artifact if (real_win_mode == WMODE_TRANS || ps->o.force_win_blend) { // 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); + // (reg_paint_in_bound = reg_bound \cap reg_paint) + ps->backend_data->ops->blur( + ps->backend_data, w->opacity, ps->backend_blur_context, + ®_paint_in_bound, ®_visible); } else { // Window itself is solid, we only need to blur the frame // region @@ -190,8 +216,8 @@ void paint_all_new(session_t *ps, struct managed_win *t, bool ignore_damage) { auto reg_blur = win_get_region_frame_local_by_val(w); pixman_region32_translate(®_blur, w->g.x, w->g.y); - // make sure reg_blur \in reg_damage - pixman_region32_intersect(®_blur, ®_blur, ®_damage); + // make sure reg_blur \in reg_paint + pixman_region32_intersect(®_blur, ®_blur, ®_paint); ps->backend_data->ops->blur(ps->backend_data, w->opacity, ps->backend_blur_context, ®_blur, ®_visible); @@ -200,8 +226,9 @@ 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, w->g.x, - w->g.y, ®_paint, ®_visible); + ps->backend_data->ops->compose(ps->backend_data, w->win_image, + w->g.x, w->g.y, + ®_paint_in_bound, ®_visible); } else { // For window image processing, we don't need to limit the process // region to damage, since the window image data is independent @@ -219,7 +246,7 @@ void paint_all_new(session_t *ps, struct managed_win *t, bool ignore_damage) { // outside of the damage region won't be painted onto target region_t reg_visible_local; pixman_region32_init(®_visible_local); - pixman_region32_intersect(®_visible_local, ®_visible, ®_damage); + pixman_region32_intersect(®_visible_local, ®_visible, ®_paint); pixman_region32_translate(®_visible_local, -w->g.x, -w->g.y); // Data outside of the bounding shape won't be visible, but it is // not necessary to limit the image operations to the bounding @@ -228,7 +255,6 @@ void paint_all_new(session_t *ps, struct managed_win *t, bool ignore_damage) { pixman_region32_intersect(®_visible_local, ®_visible_local, ®_bound_local); - // A region covers the entire window auto new_img = ps->backend_data->ops->copy( ps->backend_data, w->win_image, ®_visible_local); if (w->invert_color) { @@ -258,21 +284,22 @@ void paint_all_new(session_t *ps, struct managed_win *t, bool ignore_damage) { NULL, ®_visible_local, (double[]){w->opacity}); } ps->backend_data->ops->compose(ps->backend_data, new_img, w->g.x, - w->g.y, ®_paint, ®_visible); + 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); } pixman_region32_fini(®_bound); - pixman_region32_fini(®_paint); + pixman_region32_fini(®_paint_in_bound); } - pixman_region32_fini(®_damage); + pixman_region32_fini(®_paint); if (ps->o.monitor_repaint) { - reg_damage = get_damage(ps, false); - ps->backend_data->ops->fill(ps->backend_data, - (struct color){0.5, 0, 0, 0.5}, ®_damage); - pixman_region32_fini(®_damage); + auto reg_damage_debug = get_damage(ps, false); + ps->backend_data->ops->fill( + ps->backend_data, (struct color){0.5, 0, 0, 0.5}, ®_damage_debug); + pixman_region32_fini(®_damage_debug); } // Move the head of the damage ring @@ -285,9 +312,11 @@ void paint_all_new(session_t *ps, struct managed_win *t, bool ignore_damage) { if (ps->backend_data->ops->present) { // Present the rendered scene // Vsync is done here - ps->backend_data->ops->present(ps->backend_data); + ps->backend_data->ops->present(ps->backend_data, ®_damage); } + pixman_region32_fini(®_damage); + #ifdef DEBUG_REPAINT struct timespec now = get_time_timespec(); struct timespec diff = {0}; diff --git a/src/backend/backend.h b/src/backend/backend.h index 4e4a8e6..a5c70d4 100644 --- a/src/backend/backend.h +++ b/src/backend/backend.h @@ -114,8 +114,7 @@ struct backend_operations { void (*prepare)(backend_t *backend_data, const region_t *reg_damage); /** - * Paint the content of an image onto the (possibly buffered) - * target picture. + * Paint the content of an image onto the back buffer * * @param backend_data the backend data * @param image_data the image to paint @@ -134,10 +133,11 @@ struct backend_operations { const region_t *reg_blur, const region_t *reg_visible) attr_nonnull(1, 3, 4, 5); - /// Present the back buffer onto the screen. + /// Present part of the back buffer onto the screen. /// - /// Optional if the screen is not buffered - void (*present)(backend_t *backend_data) attr_nonnull(1); + /// @param region part of the screen that should be updated. if NULL, update the + /// whole screen + void (*present)(backend_t *backend_data, const region_t *region) attr_nonnull(1); /** * Bind a X pixmap to the backend's internal image data structure. diff --git a/src/backend/gl/gl_common.c b/src/backend/gl/gl_common.c index 13d4c5a..fceb39f 100644 --- a/src/backend/gl/gl_common.c +++ b/src/backend/gl/gl_common.c @@ -254,6 +254,8 @@ static void _gl_compose(backend_t *base, struct gl_image *img, GLuint target, // Cleanup glBindTexture(GL_TEXTURE_2D, 0); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); + glDrawBuffer(GL_BACK); if (dual_texture) { glActiveTexture(GL_TEXTURE1); @@ -276,16 +278,16 @@ static void _gl_compose(backend_t *base, struct gl_image *img, GLuint target, /// @param[in] nrects, rects rectangles /// @param[in] dst_x, dst_y origin of the OpenGL texture, affect the calculated texture /// coordinates -/// @param[in] width, height size of the OpenGL texture +/// @param[in] texture_height height of the OpenGL texture /// @param[in] root_height height of the back buffer /// @param[in] y_inverted whether the texture is y inverted /// @param[out] coord, indices output static void -x_rect_to_coords(int nrects, const rect_t *rects, int dst_x, int dst_y, int height, +x_rect_to_coords(int nrects, const rect_t *rects, int dst_x, int dst_y, int texture_height, int root_height, bool y_inverted, GLint *coord, GLuint *indices) { dst_y = root_height - dst_y; if (y_inverted) { - dst_y -= height; + dst_y -= texture_height; } for (int i = 0; i < nrects; i++) { @@ -302,8 +304,8 @@ x_rect_to_coords(int nrects, const rect_t *rects, int dst_x, int dst_y, int heig // X pixmaps might be Y inverted, invert the texture coordinates if (y_inverted) { - texture_y1 = height - texture_y1; - texture_y2 = height - texture_y2; + texture_y1 = texture_height - texture_y1; + texture_y2 = texture_height - texture_y2; } // Vertex coordinates @@ -360,7 +362,7 @@ void gl_compose(backend_t *base, void *image_data, int dst_x, int dst_y, auto indices = ccalloc(nrects * 6, GLuint); x_rect_to_coords(nrects, rects, dst_x, dst_y, img->inner->height, gd->height, img->inner->y_inverted, coord, indices); - _gl_compose(base, img, 0, coord, indices, nrects); + _gl_compose(base, img, gd->back_fbo, coord, indices, nrects); free(indices); free(coord); @@ -421,9 +423,7 @@ bool gl_blur(backend_t *base, double opacity, void *ctx, const region_t *reg_blu resize_region(reg_blur, bctx->resize_width, bctx->resize_height); 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, - width_resized = extent_resized->x2 - extent_resized->x1, - height_resized = extent_resized->y2 - extent_resized->y1; + 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) { @@ -483,22 +483,27 @@ bool gl_blur(backend_t *base, double opacity, void *ctx, const region_t *reg_blu sizeof(GLint) * 4, (void *)(sizeof(GLint) * 2)); int curr = 0; - glReadBuffer(GL_BACK); - glBindTexture(GL_TEXTURE_2D, bctx->blur_texture[0]); - // Copy the area to be blurred into tmp buffer - - int copy_tex_xoffset = 0, copy_tex_yoffset = 0, copy_tex_x = extent_resized->x1, - copy_tex_y = dst_y_resized_screen_coord; - glCopyTexSubImage2D(GL_TEXTURE_2D, 0, copy_tex_xoffset, copy_tex_yoffset, - copy_tex_x, copy_tex_y, width_resized, height_resized); - 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]); - glBindTexture(GL_TEXTURE_2D, bctx->blur_texture[curr]); + // The origin to use when sampling from the source texture + GLint texorig_x, texorig_y; + GLuint src_texture; + + if (i == 0) { + texorig_x = extent_resized->x1; + texorig_y = dst_y_resized_screen_coord; + src_texture = gd->back_texture; + } else { + texorig_x = 0; + texorig_y = 0; + src_texture = bctx->blur_texture[curr]; + } + + glBindTexture(GL_TEXTURE_2D, src_texture); glUseProgram(p->prog); if (i < bctx->npasses - 1) { // not last pass, draw into framebuffer, with resized regions @@ -522,13 +527,13 @@ bool gl_blur(backend_t *base, double opacity, void *ctx, const region_t *reg_blu // last pass, draw directly into the back buffer, with origin // regions glBindVertexArray(vao[0]); - glBindFramebuffer(GL_FRAMEBUFFER, 0); - glDrawBuffer(GL_BACK); + glBindFramebuffer(GL_FRAMEBUFFER, gd->back_fbo); glUniform1f(p->unifm_opacity, (float)opacity); glUniform2f(p->orig_loc, 0, 0); glViewport(0, 0, gd->width, gd->height); } + 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 @@ -562,12 +567,13 @@ end: const char *vertex_shader = GLSL(330, uniform mat4 projection; 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); - texcoord = in_texcoord; + texcoord = in_texcoord + texorig; } ); // clang-format on @@ -623,6 +629,14 @@ void gl_resize(struct gl_data *gd, int width, int height) { pml = glGetUniformLocationChecked(gd->fill_shader.prog, "projection"); glUniformMatrix4fv(pml, 1, false, projection_matrix[0]); + glUseProgram(gd->present_prog); + pml = glGetUniformLocationChecked(gd->present_prog, "projection"); + glUniformMatrix4fv(pml, 1, false, projection_matrix[0]); + + glBindTexture(GL_TEXTURE_2D, gd->back_texture); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, width, height, 0, GL_BGR, + GL_UNSIGNED_BYTE, NULL); + gl_check_err(); } @@ -653,8 +667,8 @@ static const char fill_vert[] = GLSL(330, /// Fill a given region in bound framebuffer. /// @param[in] y_inverted whether the y coordinates in `clip` should be inverted -static void -_gl_fill(backend_t *base, struct color c, const region_t *clip, int height, bool y_inverted) { +static void _gl_fill(backend_t *base, struct color c, const region_t *clip, GLuint target, + int height, bool y_inverted) { static const GLuint fill_vert_in_coord_loc = 0; int nrects; const rect_t *rect = pixman_region32_rectangles((region_t *)clip, &nrects); @@ -695,7 +709,9 @@ _gl_fill(backend_t *base, struct color c, const region_t *clip, int height, bool glVertexAttribPointer(fill_vert_in_coord_loc, 2, GL_INT, GL_FALSE, sizeof(*coord) * 2, (void *)0); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, target); glDrawElements(GL_TRIANGLES, nrects * 6, GL_UNSIGNED_INT, NULL); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); glDisableVertexAttribArray(fill_vert_in_coord_loc); @@ -707,7 +723,7 @@ _gl_fill(backend_t *base, struct color c, const region_t *clip, int height, bool void gl_fill(backend_t *base, struct color c, const region_t *clip) { struct gl_data *gd = (void *)base; - return _gl_fill(base, c, clip, gd->height, true); + return _gl_fill(base, c, clip, gd->back_fbo, gd->height, true); } void gl_release_image(backend_t *base, void *image_data) { @@ -870,6 +886,7 @@ void *gl_create_blur_context(backend_t *base, enum blur_method method, void *arg // Get uniform addresses pass->unifm_opacity = glGetUniformLocationChecked(pass->prog, "opacity"); pass->orig_loc = glGetUniformLocationChecked(pass->prog, "orig"); + pass->texorig_loc = glGetUniformLocationChecked(pass->prog, "texorig"); ctx->resize_width += kern->w / 2; ctx->resize_height += kern->h / 2; } @@ -881,6 +898,7 @@ void *gl_create_blur_context(backend_t *base, enum blur_method method, void *arg pass->prog = gl_create_program_from_str(vertex_shader, dummy_frag); pass->unifm_opacity = glGetUniformLocationChecked(pass->prog, "opacity"); pass->orig_loc = glGetUniformLocationChecked(pass->prog, "orig"); + pass->texorig_loc = glGetUniformLocationChecked(pass->prog, "texorig"); ctx->npasses = 2; } else { ctx->npasses = nkernels; @@ -948,6 +966,16 @@ const char *win_shader_glsl = GLSL(330, gl_FragColor = c; } ); + +const char *present_vertex_shader = GLSL(330, + uniform mat4 projection; + layout(location = 0) in vec2 coord; + out vec2 texcoord; + void main() { + gl_Position = projection * vec4(coord, 0, 1); + texcoord = coord; + } +); // clang-format on bool gl_init(struct gl_data *gd, session_t *ps) { @@ -969,14 +997,41 @@ bool gl_init(struct gl_data *gd, session_t *ps) { glClearColor(0.0f, 0.0f, 0.0f, 1.0f); glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); + glGenFramebuffers(1, &gd->back_fbo); + glGenTextures(1, &gd->back_texture); + if (!gd->back_fbo || !gd->back_texture) { + log_error("Failed to generate a framebuffer object"); + return false; + } + + glBindTexture(GL_TEXTURE_2D, gd->back_texture); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glBindTexture(GL_TEXTURE_2D, 0); + gl_win_shader_from_string(vertex_shader, win_shader_glsl, &gd->win_shader); gd->fill_shader.prog = gl_create_program_from_str(fill_vert, fill_frag); gd->fill_shader.color_loc = glGetUniformLocation(gd->fill_shader.prog, "color"); + gd->present_prog = gl_create_program_from_str(present_vertex_shader, dummy_frag); + if (!gd->present_prog) { + log_error("Failed to create the present shader"); + return false; + } + glUseProgram(gd->present_prog); + glUniform1i(glGetUniformLocationChecked(gd->present_prog, "tex"), 0); + glUseProgram(0); + // Set up the size of the viewport. We do this last because it expects the blur // textures are already set up. gl_resize(gd, ps->root_width, ps->root_height); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, gd->back_fbo); + glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, + gd->back_texture, 0); + glDrawBuffer(GL_COLOR_ATTACHMENT0); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); + gd->logger = gl_string_marker_logger_new(); if (gd->logger) { log_add_target_tls(gd->logger); @@ -1098,12 +1153,64 @@ static void gl_image_apply_alpha(backend_t *base, struct gl_image *img, glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, img->inner->texture, 0); glDrawBuffer(GL_COLOR_ATTACHMENT0); - _gl_fill(base, (struct color){0, 0, 0, 0}, reg_op, 0, false); + _gl_fill(base, (struct color){0, 0, 0, 0}, reg_op, 0, 0, false); glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); glDeleteFramebuffers(1, &fbo); } +void gl_present(backend_t *base, const region_t *region) { + auto gd = (struct gl_data *)base; + + int nrects; + const rect_t *rect = pixman_region32_rectangles((region_t *)region, &nrects); + auto coord = ccalloc(nrects * 8, GLint); + auto indices = ccalloc(nrects * 6, GLuint); + for (int i = 0; i < nrects; i++) { + // clang-format off + memcpy(&coord[i * 8], + (GLint[]){rect[i].x1, gd->height - rect[i].y2, + rect[i].x2, gd->height - rect[i].y2, + rect[i].x2, gd->height - rect[i].y1, + rect[i].x1, gd->height - rect[i].y1}, + sizeof(GLint) * 8); + // clang-format on + + GLuint u = (GLuint)(i * 4); + memcpy(&indices[i * 6], (GLuint[]){u + 0, u + 1, u + 2, u + 2, u + 3, u + 0}, + sizeof(GLuint) * 6); + } + + glUseProgram(gd->present_prog); + glBindTexture(GL_TEXTURE_2D, gd->back_texture); + + GLuint vao; + glGenVertexArrays(1, &vao); + glBindVertexArray(vao); + + GLuint bo[2]; + glGenBuffers(2, bo); + glEnableVertexAttribArray(vert_coord_loc); + glBindBuffer(GL_ARRAY_BUFFER, bo[0]); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bo[1]); + glBufferData(GL_ARRAY_BUFFER, (long)sizeof(GLint) * nrects * 8, coord, GL_STREAM_DRAW); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, (long)sizeof(GLuint) * nrects * 6, indices, + GL_STREAM_DRAW); + + glVertexAttribPointer(vert_coord_loc, 2, GL_INT, GL_FALSE, + sizeof(GLint) * 2, NULL); + glDrawElements(GL_TRIANGLES, nrects * 6, GL_UNSIGNED_INT, NULL); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + glBindVertexArray(0); + glDeleteBuffers(2, bo); + glDeleteVertexArrays(1, &vao); + + free(coord); + free(indices); +} + /// stub for backend_operations::image_op bool gl_image_op(backend_t *base, enum image_operations op, void *image_data, const region_t *reg_op, const region_t *reg_visible attr_unused, void *arg) { diff --git a/src/backend/gl/gl_common.h b/src/backend/gl/gl_common.h index 566850a..d0644d8 100644 --- a/src/backend/gl/gl_common.h +++ b/src/backend/gl/gl_common.h @@ -28,6 +28,7 @@ typedef struct { GLuint prog; GLint unifm_opacity; GLint orig_loc; + GLint texorig_loc; } gl_blur_shader_t; typedef struct { @@ -61,6 +62,8 @@ struct gl_data { int height, width; gl_win_shader_t win_shader; gl_fill_shader_t fill_shader; + GLuint back_texture, back_fbo; + GLuint present_prog; /// Called when an gl_texture is decoupled from the texture it refers. Returns /// the decoupled user_data @@ -110,6 +113,8 @@ void gl_get_blur_size(void *blur_context, 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); +void gl_present(backend_t *base, const region_t *); + static inline void gl_delete_texture(GLuint texture) { glDeleteTextures(1, &texture); } diff --git a/src/backend/gl/glx.c b/src/backend/gl/glx.c index 6f2ceb2..5518118 100644 --- a/src/backend/gl/glx.c +++ b/src/backend/gl/glx.c @@ -436,8 +436,9 @@ err: return NULL; } -static void glx_present(backend_t *base) { +static void glx_present(backend_t *base, const region_t *region attr_unused) { struct _glx_data *gd = (void *)base; + gl_present(base, region); glXSwapBuffers(gd->display, gd->target_win); // XXX there should be no need to block compton will wait for render to finish if (!gd->gl.is_nvidia) { diff --git a/src/backend/xrender/xrender.c b/src/backend/xrender/xrender.c index 2a07480..5810513 100644 --- a/src/backend/xrender/xrender.c +++ b/src/backend/xrender/xrender.c @@ -28,15 +28,14 @@ typedef struct _xrender_data { /// If vsync is enabled and supported by the current system bool vsync; xcb_visualid_t default_visual; - /// The idle fence for the present extension - xcb_sync_fence_t idle_fence; - /// The target window + /// Target window xcb_window_t target_win; - /// The painting target, it is either the root or the overlay + /// Painting target, it is either the root or the overlay xcb_render_picture_t target; - /// A back buffer - xcb_render_picture_t back[2]; - /// Age of each back buffer + /// Back buffers. Double buffer, with 1 for temporary render use + xcb_render_picture_t back[3]; + /// The back buffer that is for temporary use + /// Age of each back buffer. int buffer_age[2]; /// The back buffer we should be painting into int curr_back; @@ -104,9 +103,9 @@ 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[xd->curr_back], 0, 0, ®); - xcb_render_composite(base->c, op, img->pict, alpha_pict, xd->back[xd->curr_back], - 0, 0, 0, 0, to_i16_checked(dst_x), to_i16_checked(dst_y), + 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)); pixman_region32_fini(®); } @@ -114,10 +113,10 @@ static void compose(backend_t *base, void *img_data, int dst_x, int dst_y, static void fill(backend_t *base, struct color c, const region_t *clip) { struct _xrender_data *xd = (void *)base; const rect_t *extent = pixman_region32_extents((region_t *)clip); - x_set_picture_clip_region(base->c, xd->back[xd->curr_back], 0, 0, clip); + x_set_picture_clip_region(base->c, xd->back[2], 0, 0, clip); // color is in X fixed point representation xcb_render_fill_rectangles( - base->c, XCB_RENDER_PICT_OP_OVER, xd->back[xd->curr_back], + base->c, XCB_RENDER_PICT_OP_OVER, xd->back[2], (xcb_render_color_t){.red = (uint16_t)(c.red * 0xffff), .green = (uint16_t)(c.green * 0xffff), .blue = (uint16_t)(c.blue * 0xffff), @@ -177,7 +176,7 @@ static bool blur(backend_t *backend_data, double opacity, void *ctx_, x_set_picture_clip_region(c, tmp_picture[1], 0, 0, &clip); pixman_region32_fini(&clip); - xcb_render_picture_t src_pict = xd->back[xd->curr_back], dst_pict = tmp_picture[0]; + xcb_render_picture_t src_pict = xd->back[2], dst_pict = tmp_picture[0]; auto alpha_pict = xd->alpha_pict[(int)(opacity * MAX_ALPHA)]; int current = 0; x_set_picture_clip_region(c, src_pict, 0, 0, ®_op_resized); @@ -212,11 +211,11 @@ static bool blur(backend_t *backend_data, double opacity, void *ctx_, XCB_NONE, dst_pict, 0, 0, 0, 0, 0, 0, width_resized, height_resized); } else { - x_set_picture_clip_region(c, xd->back[xd->curr_back], 0, 0, ®_op); + x_set_picture_clip_region(c, xd->back[2], 0, 0, ®_op); // This is the last pass, and we are doing more than 1 pass xcb_render_composite(c, XCB_RENDER_PICT_OP_OVER, src_pict, - alpha_pict, xd->back[xd->curr_back], 0, 0, 0, - 0, to_i16_checked(extent_resized->x1), + alpha_pict, xd->back[2], 0, 0, 0, 0, + to_i16_checked(extent_resized->x1), to_i16_checked(extent_resized->y1), width_resized, height_resized); } @@ -232,10 +231,10 @@ static bool blur(backend_t *backend_data, double opacity, void *ctx_, // There is only 1 pass if (i == 1) { - x_set_picture_clip_region(c, xd->back[xd->curr_back], 0, 0, ®_op); + x_set_picture_clip_region(c, xd->back[2], 0, 0, ®_op); xcb_render_composite( - c, XCB_RENDER_PICT_OP_OVER, src_pict, alpha_pict, - xd->back[xd->curr_back], 0, 0, 0, 0, to_i16_checked(extent_resized->x1), + c, XCB_RENDER_PICT_OP_OVER, src_pict, alpha_pict, xd->back[2], 0, 0, + 0, 0, to_i16_checked(extent_resized->x1), to_i16_checked(extent_resized->y1), width_resized, height_resized); } @@ -301,10 +300,25 @@ static void deinit(backend_t *backend_data) { free(xd); } -static void present(backend_t *base) { +static void present(backend_t *base, const region_t *region) { struct _xrender_data *xd = (void *)base; + const rect_t *extent = pixman_region32_extents((region_t *)region); + int16_t orig_x = to_i16_checked(extent->x1), orig_y = to_i16_checked(extent->y1); + uint16_t region_width = to_u16_checked(extent->x2 - extent->x1), + region_height = to_u16_checked(extent->y2 - extent->y1); + + // compose() sets clip region on the back buffer, so clear it first + x_clear_picture_clip_region(base->c, xd->back[xd->curr_back]); + + // limit the region of update + x_set_picture_clip_region(base->c, xd->back[2], 0, 0, region); if (xd->vsync) { + // Update the back buffer first, then present + xcb_render_composite(base->c, XCB_RENDER_PICT_OP_SRC, xd->back[2], + XCB_NONE, xd->back[xd->curr_back], orig_x, orig_y, 0, + 0, orig_x, orig_y, region_width, region_height); + // Make sure we got reply from PresentPixmap before waiting for events, // to avoid deadlock auto e = xcb_request_check( @@ -342,16 +356,13 @@ static void present(backend_t *base) { } free(pev); } else { - // compose() sets clip region, so clear it first to make - // sure we update the whole screen. - x_clear_picture_clip_region(xd->base.c, xd->back[xd->curr_back]); + // No vsync needed, draw into the target picture directly + xcb_render_composite(base->c, XCB_RENDER_PICT_OP_SRC, xd->back[2], + XCB_NONE, xd->target, orig_x, orig_y, 0, 0, orig_x, + orig_y, region_width, region_height); - // TODO buffer-age-like optimization might be possible here. - // but that will require a different backend API - xcb_render_composite(base->c, XCB_RENDER_PICT_OP_SRC, - xd->back[xd->curr_back], XCB_NONE, xd->target, 0, 0, - 0, 0, 0, 0, to_u16_checked(xd->target_width), - to_u16_checked(xd->target_height)); + // Only the target picture really holds the screen content, and its + // content is always up to date. So buffer age is always 1. xd->buffer_age[xd->curr_back] = 1; } } @@ -589,9 +600,10 @@ backend_t *backend_xrender_init(session_t *ps) { xd->vsync = false; } - // We might need to do double buffering for vsync - int pixmap_needed = xd->vsync ? 2 : 1; - for (int i = 0; i < pixmap_needed; i++) { + // We might need to do double buffering for vsync, and buffer 0 and 1 are for + // double buffering. + int first_buffer_index = xd->vsync ? 0 : 2; + for (int i = first_buffer_index; i < 3; i++) { xd->back_pixmap[i] = x_create_pixmap(ps->c, pictfmt->depth, ps->root, to_u16_checked(ps->root_width), to_u16_checked(ps->root_height)); diff --git a/src/compton.c b/src/compton.c index 3975e0c..447068c 100644 --- a/src/compton.c +++ b/src/compton.c @@ -575,6 +575,11 @@ static struct managed_win *paint_preprocess(session_t *ps, bool *fade_running) { } w->prev_trans = bottom; + if (bottom) { + w->stacking_rank = bottom->stacking_rank + 1; + } else { + w->stacking_rank = 0; + } bottom = w; // If the screen is not redirected and the window has redir_ignore set, diff --git a/src/win.h b/src/win.h index d1559f3..90e6af4 100644 --- a/src/win.h +++ b/src/win.h @@ -169,6 +169,8 @@ struct managed_win { void *shadow_image; /// Pointer to the next higher window to paint. struct managed_win *prev_trans; + /// Number of windows above this window + int stacking_rank; // TODO rethink reg_ignore // Core members