From e80ff8530e03095a296c789de989f3f611413505 Mon Sep 17 00:00:00 2001 From: Yuxuan Shui Date: Tue, 8 Jan 2019 23:15:00 +0000 Subject: [PATCH] Make buffer age not glx specific Buffer age is not a glx specific concept. xrender backend can have buffer age too, if double buffering is used (required if we want to use Present). So, make buffer age a generic concept in compton is required for further backend improvements. Moved buffer age based damage aggragation out of glx as well. Signed-off-by: Yuxuan Shui --- src/backend/gl/glx.c | 57 ---------- src/common.h | 15 +-- src/compton.c | 72 ++---------- src/opengl.c | 60 ---------- src/render.c | 257 +++++++++++++++++++++++++++---------------- src/render.h | 2 +- 6 files changed, 180 insertions(+), 283 deletions(-) diff --git a/src/backend/gl/glx.c b/src/backend/gl/glx.c index ff06254..c867dbe 100644 --- a/src/backend/gl/glx.c +++ b/src/backend/gl/glx.c @@ -592,63 +592,6 @@ void glx_render_win(void *backend_data, session_t *ps, win *w, void *win_data, c gl_check_err(); } -/** - * Preprocess function before start painting. - */ -static void attr_unused glx_paint_pre(session_t *ps, region_t *preg) { - // glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - - // Get buffer age - bool trace_damage = (ps->o.glx_swap_method < 0 || ps->o.glx_swap_method > 1); - - // Trace raw damage regions - region_t newdamage; - pixman_region32_init(&newdamage); - if (trace_damage) - copy_region(&newdamage, preg); - - // We use GLX buffer_age extension to decide which pixels in - // the back buffer is reusable, and limit our redrawing - int buffer_age = 0; - - // Query GLX_EXT_buffer_age for buffer age - if (ps->o.glx_swap_method == SWAPM_BUFFER_AGE) { - unsigned val = 0; - glXQueryDrawable(ps->dpy, get_tgt_window(ps), GLX_BACK_BUFFER_AGE_EXT, &val); - buffer_age = val; - } - - // Buffer age too high - if (buffer_age > CGLX_MAX_BUFFER_AGE + 1) - buffer_age = 0; - - assert(buffer_age >= 0); - - if (buffer_age) { - // Determine paint area - for (int i = 0; i < buffer_age - 1; ++i) - pixman_region32_union(preg, preg, &ps->all_damage_last[i]); - } else - // buffer_age == 0 means buffer age is not available, paint everything - copy_region(preg, &ps->screen_reg); - - if (trace_damage) { - // XXX use a circular queue instead of memmove - pixman_region32_fini(&ps->all_damage_last[CGLX_MAX_BUFFER_AGE - 1]); - memmove(ps->all_damage_last + 1, ps->all_damage_last, - (CGLX_MAX_BUFFER_AGE - 1) * sizeof(region_t *)); - ps->all_damage_last[0] = newdamage; - } - - // gl_set_clip(ps, preg); - -#ifdef DEBUG_GLX_PAINTREG - glx_render_color(ps, 0, 0, ps->root_width, ps->root_height, 0, *preg, NULL); -#endif - - gl_check_err(); -} - backend_info_t glx_backend = { .init = glx_init, .deinit = glx_deinit, diff --git a/src/common.h b/src/common.h index 6f01f2b..7aa1f77 100644 --- a/src/common.h +++ b/src/common.h @@ -427,9 +427,11 @@ typedef struct session { /// Program start time. struct timeval time_start; /// The region needs to painted on next paint. - region_t all_damage; + region_t *damage; /// The region damaged on the last paint. - region_t all_damage_last[CGLX_MAX_BUFFER_AGE]; + region_t *damage_ring; + /// Number of damage regions we track + int ndamage; /// Whether all windows are currently redirected. bool redirected; /// Pre-generated alpha pictures. @@ -897,15 +899,6 @@ find_focused(session_t *ps) { return NULL; } -/** - * Free all regions in ps->all_damage_last . - */ -static inline void -free_all_damage_last(session_t *ps) { - for (int i = 0; i < CGLX_MAX_BUFFER_AGE; ++i) - pixman_region32_clear(&ps->all_damage_last[i]); -} - /** * Check if a rectangle includes the whole screen. */ diff --git a/src/compton.c b/src/compton.c index d5dcc52..aff38c1 100644 --- a/src/compton.c +++ b/src/compton.c @@ -206,38 +206,6 @@ get_time_ms(void) { return tv.tv_sec % SEC_WRAP * 1000 + tv.tv_usec / 1000; } -/** - * Resize a region. - */ -static inline void -resize_region(session_t *ps, region_t *region, short mod) { - if (!mod || !region) return; - // Loop through all rectangles - int nrects; - int nnewrects = 0; - pixman_box32_t *rects = pixman_region32_rectangles(region, &nrects); - auto newrects = ccalloc(nrects, pixman_box32_t); - for (int i = 0; i < nrects; i++) { - int x1 = max_i(rects[i].x1 - mod, 0); - int y1 = max_i(rects[i].y1 - mod, 0); - int x2 = min_i(rects[i].x2 + mod, ps->root_width); - int y2 = min_i(rects[i].y2 + mod, ps->root_height); - int wid = x2 - x1; - int hei = y2 - y1; - if (wid <= 0 || hei <= 0) - continue; - newrects[nnewrects] = (pixman_box32_t) { - .x1 = x1, .x2 = x2, .y1 = y1, .y2 = y2 - }; - ++nnewrects; - } - - pixman_region32_fini(region); - pixman_region32_init_rects(region, newrects, nnewrects); - - free(newrects); -} - /** * Get the Xinerama screen a window is on. * @@ -345,7 +313,7 @@ void add_damage(session_t *ps, const region_t *damage) { if (!damage) return; - pixman_region32_union(&ps->all_damage, &ps->all_damage, (region_t *)damage); + pixman_region32_union(ps->damage, ps->damage, (region_t *)damage); } // === Fading === @@ -1082,7 +1050,10 @@ configure_win(session_t *ps, xcb_configure_notify_event_t *ce) { rebuild_screen_reg(ps); rebuild_shadow_exclude_reg(ps); - free_all_damage_last(ps); + for (int i = 0; i < ps->ndamage; i++) { + pixman_region32_clear(&ps->damage_ring[i]); + } + ps->damage = ps->damage_ring + ps->ndamage - 1; // Re-redirect screen if required if (ps->o.reredir_on_root_change && ps->redirected) { @@ -1281,7 +1252,6 @@ ev_xcb_error(session_t attr_unused *ps, xcb_generic_error_t *err) { static void expose_root(session_t *ps, const rect_t *rects, int nrects) { - free_all_damage_last(ps); region_t region; pixman_region32_init_rects(®ion, rects, nrects); add_damage(ps, ®ion); @@ -2424,25 +2394,9 @@ _draw_callback(EV_P_ session_t *ps, int revents) { } // If the screen is unredirected, free all_damage to stop painting - if (!ps->redirected || ps->o.stoppaint_force == ON) - pixman_region32_clear(&ps->all_damage); - - if (pixman_region32_not_empty(&ps->all_damage)) { - region_t all_damage_orig, *region_real = NULL; - pixman_region32_init(&all_damage_orig); - - // keep a copy of non-resized all_damage for region_real - if (ps->o.resize_damage > 0) { - copy_region(&all_damage_orig, &ps->all_damage); - resize_region(ps, &ps->all_damage, ps->o.resize_damage); - region_real = &all_damage_orig; - } - + if (ps->redirected && ps->o.stoppaint_force != ON) { static int paint = 0; - paint_all(ps, &ps->all_damage, region_real, t); - - pixman_region32_clear(&ps->all_damage); - pixman_region32_fini(&all_damage_orig); + paint_all(ps, t, false); paint++; if (ps->o.benchmark && paint >= ps->o.benchmark) @@ -2710,9 +2664,6 @@ session_init(session_t *ps_old, int argc, char **argv) { *ps = s_def; ps->loop = EV_DEFAULT; pixman_region32_init(&ps->screen_reg); - pixman_region32_init(&ps->all_damage); - for (int i = 0; i < CGLX_MAX_BUFFER_AGE; i ++) - pixman_region32_init(&ps->all_damage_last[i]); ps_g = ps; ps->ignore_tail = &ps->ignore_head; @@ -3193,9 +3144,10 @@ session_destroy(session_t *ps) { free_paint(ps, &ps->tgt_buffer); pixman_region32_fini(&ps->screen_reg); - pixman_region32_fini(&ps->all_damage); - for (int i = 0; i < CGLX_MAX_BUFFER_AGE; ++i) - pixman_region32_fini(&ps->all_damage_last[i]); + for (int i = 0; i < ps->ndamage; ++i) + pixman_region32_fini(&ps->damage_ring[i]); + ps->ndamage = 0; + ps->damage_ring = ps->damage = NULL; free(ps->expose_rects); free(ps->o.config_file); @@ -3304,7 +3256,7 @@ session_run(session_t *ps) { t = paint_preprocess(ps, ps->list); if (ps->redirected) - paint_all(ps, NULL, NULL, t); + paint_all(ps, t, true); // In benchmark mode, we want draw_idle handler to always be active if (ps->o.benchmark) diff --git a/src/opengl.c b/src/opengl.c index 3f63cd7..6665491 100644 --- a/src/opengl.c +++ b/src/opengl.c @@ -857,66 +857,6 @@ glx_release_pixmap(session_t *ps, glx_texture_t *ptex) { glx_check_err(ps); } -/** - * Preprocess function before start painting. - */ -void -glx_paint_pre(session_t *ps, region_t *preg) { - ps->psglx->z = 0.0; - // glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - - // Get buffer age - bool trace_damage = (ps->o.glx_swap_method < 0 || ps->o.glx_swap_method > 1); - - // Trace raw damage regions - region_t newdamage; - pixman_region32_init(&newdamage); - if (trace_damage) - copy_region(&newdamage, preg); - - // We use GLX buffer_age extension to decide which pixels in - // the back buffer is reusable, and limit our redrawing - int buffer_age = 0; - - // Query GLX_EXT_buffer_age for buffer age - if (ps->o.glx_swap_method == SWAPM_BUFFER_AGE) { - unsigned val = 0; - glXQueryDrawable(ps->dpy, get_tgt_window(ps), - GLX_BACK_BUFFER_AGE_EXT, &val); - buffer_age = val; - } - - // Buffer age too high - if (buffer_age > CGLX_MAX_BUFFER_AGE + 1) - buffer_age = 0; - - assert(buffer_age >= 0); - - if (buffer_age) { - // Determine paint area - for (int i = 0; i < buffer_age - 1; ++i) - pixman_region32_union(preg, preg, &ps->all_damage_last[i]); - } else - // buffer_age == 0 means buffer age is not available, paint everything - copy_region(preg, &ps->screen_reg); - - if (trace_damage) { - // XXX use a circular queue instead of memmove - pixman_region32_fini(&ps->all_damage_last[CGLX_MAX_BUFFER_AGE - 1]); - memmove(ps->all_damage_last + 1, ps->all_damage_last, - (CGLX_MAX_BUFFER_AGE - 1) * sizeof(region_t)); - ps->all_damage_last[0] = newdamage; - } - - glx_set_clip(ps, preg); - -#ifdef DEBUG_GLX_PAINTREG - glx_render_color(ps, 0, 0, ps->root_width, ps->root_height, 0, *preg, NULL); -#endif - - glx_check_err(ps); -} - /** * Set clipping region on the target window. */ diff --git a/src/render.c b/src/render.c index ae6cd07..2c1a186 100644 --- a/src/render.c +++ b/src/render.c @@ -59,6 +59,28 @@ static inline bool bkend_use_xrender(session_t *ps) { return BKEND_XRENDER == ps->o.backend || BKEND_XR_GLX_HYBRID == ps->o.backend; } +int maximum_buffer_age(session_t *ps) { + if (bkend_use_glx(ps) && ps->o.glx_swap_method == SWAPM_BUFFER_AGE) { + return CGLX_MAX_BUFFER_AGE; + } + return 1; +} + +static int get_buffer_age(session_t *ps) { +#ifdef CONFIG_OPENGL + if (bkend_use_glx(ps)) { + if (ps->o.glx_swap_method == SWAPM_BUFFER_AGE) { + unsigned int val; + glXQueryDrawable(ps->dpy, get_tgt_window(ps), GLX_BACK_BUFFER_AGE_EXT, &val); + return (int)val ?: -1; + } else { + return -1; + } + } +#endif + return 1; +} + /** * Reset filter on a Picture. */ @@ -68,6 +90,7 @@ static inline void xrfilter_reset(session_t *ps, xcb_render_picture_t p) { #undef FILTER } +/// Set the input/output clip region of the target buffer (not the actual target!) static inline void attr_nonnull(1, 2) set_tgt_clip(session_t *ps, region_t *reg) { switch (ps->o.backend) { case BKEND_XRENDER: @@ -113,8 +136,7 @@ void render(session_t *ps, int x, int y, int dx, int dy, int wid, int hei, doubl int alpha_step = opacity * MAX_ALPHA; xcb_render_picture_t alpha_pict = ps->alpha_picts[alpha_step]; if (alpha_step != 0) { - int op = ((!argb && !alpha_pict) ? XCB_RENDER_PICT_OP_SRC - : XCB_RENDER_PICT_OP_OVER); + int op = ((!argb && !alpha_pict) ? XCB_RENDER_PICT_OP_SRC : XCB_RENDER_PICT_OP_OVER); xcb_render_composite(ps->c, op, pict, alpha_pict, ps->tgt_buffer.pict, x, y, 0, 0, dx, dy, wid, hei); } @@ -131,9 +153,8 @@ void render(session_t *ps, int x, int y, int dx, int dy, int wid, int hei, doubl } } -static inline void -paint_region(session_t *ps, win *w, int x, int y, int wid, int hei, double opacity, - const region_t *reg_paint, xcb_render_picture_t pict) { +static inline void paint_region(session_t *ps, win *w, int x, int y, int wid, int hei, double opacity, + const region_t *reg_paint, xcb_render_picture_t pict) { const int dx = (w ? w->g.x : 0) + x; const int dy = (w ? w->g.y : 0) + y; const bool argb = (w && (win_has_alpha(w) || ps->o.force_win_blend)); @@ -200,8 +221,7 @@ void paint_one(session_t *ps, win *w, const region_t *reg_paint) { // Let glx_bind_pixmap() determine pixmap size, because if the user // is resizing windows, the width and height we get may not be up-to-date, // causing the jittering issue M4he reported in #7. - if (!paint_bind_tex(ps, &w->paint, 0, 0, 0, - (!ps->o.glx_no_rebind_pixmap && w->pixmap_damaged))) { + if (!paint_bind_tex(ps, &w->paint, 0, 0, 0, (!ps->o.glx_no_rebind_pixmap && w->pixmap_damaged))) { log_error("Failed to bind texture for window %#010x.", w->id); } w->pixmap_damaged = false; @@ -229,15 +249,15 @@ void paint_one(session_t *ps, win *w, const region_t *reg_paint) { pixman_region32_init(®); pixman_region32_copy(®, (region_t *)reg_paint); pixman_region32_translate(®, -x, -y); - // FIXME XFixesSetPictureClipRegion(ps->dpy, newpict, 0, 0, reg); + // FIXME XFixesSetPictureClipRegion(ps->dpy, newpict, 0, + // 0, reg); pixman_region32_fini(®); } xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_SRC, pict, XCB_NONE, newpict, 0, 0, 0, 0, 0, 0, wid, hei); - xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_DIFFERENCE, - ps->white_picture, XCB_NONE, newpict, 0, 0, - 0, 0, 0, 0, wid, hei); + xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_DIFFERENCE, ps->white_picture, + XCB_NONE, newpict, 0, 0, 0, 0, 0, 0, wid, hei); // We use an extra PictOpInReverse operation to get correct // pixel alpha. There could be a better solution. if (win_has_alpha(w)) @@ -261,8 +281,7 @@ void paint_one(session_t *ps, win *w, const region_t *reg_paint) { const int r = extents.right; #define COMP_BDR(cx, cy, cwid, chei) \ - paint_region(ps, w, (cx), (cy), (cwid), (chei), w->frame_opacity *dopacity, \ - reg_paint, pict) + paint_region(ps, w, (cx), (cy), (cwid), (chei), w->frame_opacity *dopacity, reg_paint, pict) // Sanitize the margins, in case some broken WM makes // top_width + bottom_width > height in some cases. @@ -353,8 +372,7 @@ void paint_one(session_t *ps, win *w, const region_t *reg_paint) { } break; #ifdef CONFIG_OPENGL case BKEND_GLX: - glx_dim_dst(ps, x, y, wid, hei, ps->psglx->z - 0.7, dim_opacity, - reg_paint); + glx_dim_dst(ps, x, y, wid, hei, ps->psglx->z - 0.7, dim_opacity, reg_paint); break; #endif default: assert(false); @@ -380,9 +398,8 @@ static bool get_root_tile(session_t *ps) { // Get the values of background attributes for (int p = 0; background_props_str[p]; p++) { - winprop_t prop = - wid_get_prop(ps, ps->root, get_atom(ps, background_props_str[p]), 1L, - XCB_ATOM_PIXMAP, 32); + winprop_t prop = wid_get_prop( + ps, ps->root, get_atom(ps, background_props_str[p]), 1L, XCB_ATOM_PIXMAP, 32); if (prop.nitems) { pixmap = *prop.p32; fill = false; @@ -465,15 +482,13 @@ static bool win_build_shadow(session_t *ps, win *w, double opacity) { xcb_render_picture_t shadow_picture = XCB_NONE, shadow_picture_argb = XCB_NONE; xcb_gcontext_t gc = XCB_NONE; - shadow_image = - make_shadow(ps->c, ps->gaussian_map, opacity, width, height); + shadow_image = make_shadow(ps->c, ps->gaussian_map, opacity, width, height); if (!shadow_image) { log_error("failed to make shadow"); return XCB_NONE; } - shadow_pixmap = - x_create_pixmap(ps, 8, ps->root, shadow_image->width, shadow_image->height); + shadow_pixmap = x_create_pixmap(ps, 8, ps->root, shadow_image->width, shadow_image->height); shadow_pixmap_argb = x_create_pixmap(ps, 32, ps->root, shadow_image->width, shadow_image->height); @@ -570,9 +585,8 @@ static inline void normalize_conv_kern(int wid, int hei, xcb_render_fixed_t *ker * * @return true if successful, false otherwise */ -static bool -xr_blur_dst(session_t *ps, xcb_render_picture_t tgt_buffer, int x, int y, int wid, - int hei, xcb_render_fixed_t **blur_kerns, const region_t *reg_clip) { +static bool xr_blur_dst(session_t *ps, xcb_render_picture_t tgt_buffer, int x, int y, int wid, + int hei, xcb_render_fixed_t **blur_kerns, const region_t *reg_clip) { assert(blur_kerns[0]); // Directly copying from tgt_buffer to it does not work, so we create a @@ -600,12 +614,10 @@ xr_blur_dst(session_t *ps, xcb_render_picture_t tgt_buffer, int x, int y, int wi // be applied on source picture, to get the nearby pixels outside the // window. xcb_render_set_picture_filter(ps->c, src_pict, strlen(XRFILTER_CONVOLUTION), - XRFILTER_CONVOLUTION, kwid * khei + 2, - convolution_blur); - xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_SRC, src_pict, XCB_NONE, - dst_pict, (rd_from_tgt ? x : 0), - (rd_from_tgt ? y : 0), 0, 0, (rd_from_tgt ? 0 : x), - (rd_from_tgt ? 0 : y), wid, hei); + XRFILTER_CONVOLUTION, kwid * khei + 2, convolution_blur); + xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_SRC, src_pict, XCB_NONE, dst_pict, + (rd_from_tgt ? x : 0), (rd_from_tgt ? y : 0), 0, 0, + (rd_from_tgt ? 0 : x), (rd_from_tgt ? 0 : y), wid, hei); xrfilter_reset(ps, src_pict); { @@ -672,12 +684,10 @@ static inline void win_blur_background(session_t *ps, win *w, xcb_render_picture } // Modify the factor of the center pixel - kern_src[2 + (khei / 2) * kwid + kwid / 2] = - DOUBLE_TO_XFIXED(factor_center); + kern_src[2 + (khei / 2) * kwid + kwid / 2] = DOUBLE_TO_XFIXED(factor_center); // Copy over - memcpy(kern_dst, kern_src, - (kwid * khei + 2) * sizeof(xcb_render_fixed_t)); + memcpy(kern_dst, kern_src, (kwid * khei + 2) * sizeof(xcb_render_fixed_t)); normalize_conv_kern(kwid, khei, kern_dst + 2); } @@ -708,10 +718,40 @@ static inline void win_blur_background(session_t *ps, win *w, xcb_render_picture } } +/** + * Resize a region. + */ +static inline void resize_region(region_t *region, short mod) { + if (!mod || !region) + return; + // Loop through all rectangles + int nrects; + int nnewrects = 0; + pixman_box32_t *rects = pixman_region32_rectangles(region, &nrects); + auto newrects = ccalloc(nrects, pixman_box32_t); + for (int i = 0; i < nrects; i++) { + int x1 = rects[i].x1 - mod; + int y1 = rects[i].y1 - mod; + int x2 = rects[i].x2 + mod; + int y2 = rects[i].y2 + mod; + int wid = x2 - x1; + int hei = y2 - y1; + if (wid <= 0 || hei <= 0) + continue; + newrects[nnewrects] = (pixman_box32_t){.x1 = x1, .x2 = x2, .y1 = y1, .y2 = y2}; + ++nnewrects; + } + + pixman_region32_fini(region); + pixman_region32_init_rects(region, newrects, nnewrects); + + free(newrects); +} + /// paint all windows /// region = ?? /// region_real = the damage region -void paint_all(session_t *ps, region_t *region, const region_t *region_real, win *const t) { +void paint_all(session_t *ps, win *const t, bool ignore_damage) { if (ps->o.xrender_sync_fence) { if (!x_fence_sync(ps, ps->sync_fence)) { log_error("x_fence_sync failed, xrender-sync-fence will be " @@ -722,24 +762,32 @@ void paint_all(session_t *ps, region_t *region, const region_t *region_real, win } } - if (!region_real) { - region_real = region; + region_t region; + pixman_region32_init(®ion); + int buffer_age = get_buffer_age(ps); + if (buffer_age == -1 || buffer_age > ps->ndamage || ignore_damage) { + pixman_region32_copy(®ion, &ps->screen_reg); + } else { + for (int i = 0; i < get_buffer_age(ps); i++) { + const int curr = ((ps->damage - ps->damage_ring) + i) % ps->ndamage; + pixman_region32_union(®ion, ®ion, &ps->damage_ring[curr]); + } + } + + if (!pixman_region32_not_empty(®ion)) { + return; } #ifdef DEBUG_REPAINT static struct timespec last_paint = {0}; #endif - if (!region) - region_real = region = &ps->screen_reg; - else - // Remove the damaged area out of screen - pixman_region32_intersect(region, region, &ps->screen_reg); + if (ps->o.resize_damage > 0) { + resize_region(®ion, ps->o.resize_damage); + } -#ifdef CONFIG_OPENGL - if (bkend_use_glx(ps)) - glx_paint_pre(ps, region); -#endif + // Remove the damaged area out of screen + pixman_region32_intersect(®ion, ®ion, &ps->screen_reg); if (!paint_isvalid(ps, &ps->tgt_buffer)) { if (!ps->tgt_buffer.pixmap) { @@ -759,26 +807,34 @@ void paint_all(session_t *ps, region_t *region, const region_t *region_real, win } if (BKEND_XRENDER == ps->o.backend) { - x_set_picture_clip_region(ps, ps->tgt_picture, 0, 0, region_real); + x_set_picture_clip_region(ps, ps->tgt_picture, 0, 0, ®ion); } +#ifdef CONFIG_OPENGL + if (bkend_use_glx(ps)) { + ps->psglx->z = 0.0; + } +#endif + region_t reg_tmp, *reg_paint; pixman_region32_init(®_tmp); if (t) { - // Calculate the region upon which the root window is to be painted - // based on the ignore region of the lowest window, if available - pixman_region32_subtract(®_tmp, region, t->reg_ignore); + // Calculate the region upon which the root window is to be + // painted based on the ignore region of the lowest window, if + // available + pixman_region32_subtract(®_tmp, ®ion, t->reg_ignore); reg_paint = ®_tmp; } else { - reg_paint = region; + reg_paint = ®ion; } set_tgt_clip(ps, reg_paint); paint_root(ps, reg_paint); // Windows are sorted from bottom to top - // Each window has a reg_ignore, which is the region obscured by all the windows - // on top of that window. This is used to reduce the number of pixels painted. + // Each window has a reg_ignore, which is the region obscured by all the + // windows on top of that window. This is used to reduce the number of + // pixels painted. // // Whether this is beneficial is to be determined XXX for (win *w = t; w; w = w->prev_trans) { @@ -790,39 +846,39 @@ void paint_all(session_t *ps, region_t *region, const region_t *region_real, win if (!win_build_shadow(ps, w, 1)) log_error("build shadow failed"); - // Shadow doesn't need to be painted underneath the body of - // the window Because no one can see it - pixman_region32_subtract(®_tmp, region, w->reg_ignore); + // Shadow doesn't need to be painted underneath the body + // of the windows above. Because no one can see it + pixman_region32_subtract(®_tmp, ®ion, w->reg_ignore); // Mask out the region we don't want shadow on if (pixman_region32_not_empty(&ps->shadow_exclude_reg)) - pixman_region32_subtract(®_tmp, ®_tmp, - &ps->shadow_exclude_reg); + pixman_region32_subtract(®_tmp, ®_tmp, &ps->shadow_exclude_reg); - // Might be worth while to crop the region to shadow border - pixman_region32_intersect_rect( - ®_tmp, ®_tmp, w->g.x + w->shadow_dx, - w->g.y + w->shadow_dy, w->shadow_width, w->shadow_height); + // Might be worth while to crop the region to shadow + // border + pixman_region32_intersect_rect(®_tmp, ®_tmp, w->g.x + w->shadow_dx, + w->g.y + w->shadow_dy, + w->shadow_width, w->shadow_height); - // Mask out the body of the window from the shadow if needed - // Doing it here instead of in make_shadow() for saving GPU - // power and handling shaped windows (XXX unconfirmed) + // Mask out the body of the window from the shadow if + // needed Doing it here instead of in make_shadow() for + // saving GPU power and handling shaped windows (XXX + // unconfirmed) if (!ps->o.wintype_option[w->window_type].full_shadow) pixman_region32_subtract(®_tmp, ®_tmp, &bshape); #ifdef CONFIG_XINERAMA if (ps->o.xinerama_shadow_crop && w->xinerama_scr >= 0 && w->xinerama_scr < ps->xinerama_nscrs) - // There can be a window where number of screens is - // updated, but the screen number attached to the - // windows have not. + // There can be a window where number of screens + // is updated, but the screen number attached to + // the windows have not. // - // Window screen number will be updated eventually, - // so here we just check to make sure we don't access - // out of bounds. + // Window screen number will be updated + // eventually, so here we just check to make sure + // we don't access out of bounds. pixman_region32_intersect( - ®_tmp, ®_tmp, - &ps->xinerama_scr_regs[w->xinerama_scr]); + ®_tmp, ®_tmp, &ps->xinerama_scr_regs[w->xinerama_scr]); #endif // Detect if the region is empty before painting @@ -832,10 +888,11 @@ void paint_all(session_t *ps, region_t *region, const region_t *region_real, win } } - // Calculate the region based on the reg_ignore of the next (higher) - // window and the bounding region - // XXX XXX - pixman_region32_subtract(®_tmp, region, w->reg_ignore); + // Calculate the paint region based on the reg_ignore of the current + // window and its bounding region. + // Remeber, reg_ignore is the union of all windows above the current + // window. + pixman_region32_subtract(®_tmp, ®ion, w->reg_ignore); pixman_region32_intersect(®_tmp, ®_tmp, &bshape); pixman_region32_fini(&bshape); @@ -855,6 +912,13 @@ void paint_all(session_t *ps, region_t *region, const region_t *region_real, win // Free up all temporary regions pixman_region32_fini(®_tmp); + // Move the head of the damage ring + ps->damage = ps->damage - 1; + if (ps->damage < ps->damage_ring) { + ps->damage = ps->damage_ring + ps->ndamage - 1; + } + pixman_region32_clear(ps->damage); + // Do this as early as possible set_tgt_clip(ps, &ps->screen_reg); @@ -886,14 +950,8 @@ void paint_all(session_t *ps, region_t *region, const region_t *region_real, win // the paint region. This is not very efficient, but since // it's for debug only, we don't really care - // First, we clear tgt_buffer.pict's clip region, since we - // want to copy everything - x_set_picture_clip_region(ps, ps->tgt_buffer.pict, 0, 0, - &ps->screen_reg); - - // Then we create a new picture, and copy content to it - xcb_render_pictforminfo_t *pictfmt = - x_get_pictform_for_visual(ps, ps->vis); + // First we create a new picture, and copy content from the buffer to it + xcb_render_pictforminfo_t *pictfmt = x_get_pictform_for_visual(ps, ps->vis); xcb_render_picture_t new_pict = x_create_picture_with_pictfmt( ps, ps->root_width, ps->root_height, pictfmt, 0, NULL); xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_SRC, @@ -901,22 +959,22 @@ void paint_all(session_t *ps, region_t *region, const region_t *region_real, win 0, 0, 0, 0, ps->root_width, ps->root_height); // Next, we set the region of paint and highlight it - x_set_picture_clip_region(ps, new_pict, 0, 0, region_real); - xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_OVER, - ps->white_picture, + x_set_picture_clip_region(ps, new_pict, 0, 0, ®ion); + xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_OVER, ps->white_picture, ps->alpha_picts[MAX_ALPHA / 2], new_pict, 0, 0, 0, 0, 0, 0, ps->root_width, ps->root_height); - // Finally, clear clip region and put the whole thing on screen + // Finally, clear clip regions of new_pict and the screen, and put + // the whole thing on screen x_set_picture_clip_region(ps, new_pict, 0, 0, &ps->screen_reg); + x_set_picture_clip_region(ps, ps->tgt_picture, 0, 0, &ps->screen_reg); xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_SRC, new_pict, XCB_NONE, ps->tgt_picture, 0, 0, 0, 0, 0, 0, ps->root_width, ps->root_height); xcb_render_free_picture(ps->c, new_pict); } else - xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_SRC, - ps->tgt_buffer.pict, XCB_NONE, - ps->tgt_picture, 0, 0, 0, 0, 0, 0, + xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_SRC, ps->tgt_buffer.pict, + XCB_NONE, ps->tgt_picture, 0, 0, 0, 0, 0, 0, ps->root_width, ps->root_height); break; #ifdef CONFIG_OPENGL @@ -936,7 +994,7 @@ void paint_all(session_t *ps, region_t *region, const region_t *region_real, win glFlush(); glXWaitX(); glx_render(ps, ps->tgt_buffer.ptex, 0, 0, 0, 0, ps->root_width, - ps->root_height, 0, 1.0, false, false, region_real, NULL); + ps->root_height, 0, 1.0, false, false, ®ion, NULL); // falls through case BKEND_GLX: glXSwapBuffers(ps->dpy, get_tgt_window(ps)); break; #endif @@ -967,6 +1025,9 @@ void paint_all(session_t *ps, region_t *region, const region_t *region_real, win log_trace(" %#010lx", w->id); #endif + // Free the paint region + pixman_region32_fini(®ion); + // Check if fading is finished on all painted windows { win *pprev = NULL; @@ -1095,6 +1156,14 @@ bool init_render(session_t *ps) { return false; } } + + ps->ndamage = maximum_buffer_age(ps); + ps->damage_ring = ccalloc(ps->ndamage, region_t); + ps->damage = ps->damage_ring + ps->ndamage - 1; + + for (int i = 0; i < ps->ndamage; i++) { + pixman_region32_init(&ps->damage_ring[i]); + } return true; } diff --git a/src/render.h b/src/render.h index c7b798e..d1273f4 100644 --- a/src/render.h +++ b/src/render.h @@ -27,7 +27,7 @@ void paint_one(session_t *ps, win *w, const region_t *reg_paint); void -paint_all(session_t *ps, region_t *region, const region_t *region_real, win * const t); +paint_all(session_t *ps, win * const t, bool ignore_damage); void free_picture(xcb_connection_t *c, xcb_render_picture_t *p);