From 7e488d813c7d32e9226097bb1c519a9fd1e60333 Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Thu, 12 Sep 2013 21:23:20 +0800 Subject: [PATCH] Bug fix #140: Possible fix for CPU usage with --unredir-possible-delay - Possible fix for high CPU usage with a low --unredir-possible-delay. Thanks to Feltzer for reporting. (#140) - Rewrite some parts to (hopefully) increase performance, especially with --unredir-if-possible. - Known issue: With GTX 670 and x11-drivers/nvidia-drivers-325.15, and compton --unredir-if-possible --config /dev/null, if you send a SIGUSR1 to compton when a full-screen solid window is there, in which case compton either redirects then immediately unredirects the screen, or just don't redirect it altogether, X freezes after compton unredirects the screen. Requests sent by other X clients are not responded until compton is killed, which indicates the possibility of a bug in X. Attaching to X process shows X is on ./os/waitFor.c. Backend does not matter. --paint-on-overlay fixes the issue somehow. compton-git-v0.1_beta1-5-g4600f43-2013-08-28 doesn't exhibit the issue, but it's probably timing-related. --- compton.c | 106 ++++++++++++++++++++++++++++++------------------------ 1 file changed, 60 insertions(+), 46 deletions(-) diff --git a/compton.c b/compton.c index b8f1fc6d5..92378b047 100644 --- a/compton.c +++ b/compton.c @@ -1102,9 +1102,16 @@ paint_preprocess(session_t *ps, win *list) { next = w->next; opacity_t opacity_old = w->opacity; - // Destroy reg_ignore on all windows if they should expire - if (ps->reg_ignore_expire) - free_region(ps, &w->reg_ignore); + // Data expiration + { + // Remove built shadow if needed + if (w->flags & WFLAG_SIZE_CHANGE) + free_paint(ps, &w->shadow_paint); + + // Destroy reg_ignore on all windows if they should expire + if (ps->reg_ignore_expire) + free_region(ps, &w->reg_ignore); + } // Update window opacity target and dim state if asked if (WFLAG_OPCT_CHANGE & w->flags) { @@ -1115,43 +1122,33 @@ paint_preprocess(session_t *ps, win *list) { // Run fading run_fade(ps, w, steps); + // Opacity will not change, from now on. + // Give up if it's not damaged or invisible, or it's unmapped and its - // pixmap is gone (for example due to a ConfigureNotify) + // pixmap is gone (for example due to a ConfigureNotify), or when it's + // excluded if (!w->damaged || w->a.x + w->a.width < 1 || w->a.y + w->a.height < 1 || w->a.x >= ps->root_width || w->a.y >= ps->root_height - || ((IsUnmapped == w->a.map_state || w->destroyed) - && !w->paint.pixmap)) { + || ((IsUnmapped == w->a.map_state || w->destroyed) && !w->paint.pixmap) + || get_alpha_pict_o(ps, w->opacity) == ps->alpha_picts[0] + || w->paint_excluded) to_paint = false; - } - to_paint = to_paint && !w->paint_excluded; + // to_paint will never change afterward - if (to_paint) { - // If opacity changes - if (w->opacity != opacity_old) { - win_determine_mode(ps, w); - add_damage_win(ps, w); - } - - if (get_alpha_pict_o(ps, w->opacity) == ps->alpha_picts[0]) - to_paint = false; - } + // Determine mode as early as possible + if (to_paint && (!w->to_paint || w->opacity != opacity_old)) + win_determine_mode(ps, w); if (to_paint) { // Fetch bounding region - if (!w->border_size) { + if (!w->border_size) w->border_size = border_size(ps, w, true); - } // Fetch window extents - if (!w->extents) { + if (!w->extents) w->extents = win_extents(ps, w); - // If w->extents does not exist, the previous add_damage_win() - // call when opacity changes has no effect, so redo it here. - if (w->opacity != opacity_old) - add_damage_win(ps, w); - } // Calculate frame_opacity { @@ -1164,6 +1161,8 @@ paint_preprocess(session_t *ps, win *list) { else w->frame_opacity = 0.0; + // Destroy all reg_ignore above when frame opaque state changes on + // SOLID mode if (w->to_paint && WMODE_SOLID == mode_old && (0.0 == frame_opacity_old) != (0.0 == w->frame_opacity)) ps->reg_ignore_expire = true; @@ -1174,24 +1173,18 @@ paint_preprocess(session_t *ps, win *list) { w->shadow_opacity = ps->o.shadow_opacity * w->frame_opacity; else w->shadow_opacity = ps->o.shadow_opacity * get_opacity_percent(w); - - // Rebuild shadow if necessary - if (w->flags & WFLAG_SIZE_CHANGE) { - free_paint(ps, &w->shadow_paint); - } - - if (w->shadow && !paint_isvalid(ps, &w->shadow_paint)) - win_build_shadow(ps, w, 1); } + // Add window to damaged area if its painting status changes + // or opacity changes + if (to_paint != w->to_paint || w->opacity != opacity_old) + add_damage_win(ps, w); + + // Destroy all reg_ignore above when window mode changes if ((to_paint && WMODE_SOLID == w->mode) != (w->to_paint && WMODE_SOLID == mode_old)) ps->reg_ignore_expire = true; - // Add window to damaged area if its painting status changes - if (to_paint != w->to_paint) - add_damage_win(ps, w); - if (to_paint) { // Generate ignore region for painting to reduce GPU load if (ps->reg_ignore_expire || !w->to_paint) { @@ -1264,6 +1257,10 @@ paint_preprocess(session_t *ps, win *list) { if (UNSET != ps->o.redirected_force) unredir_possible = !ps->o.redirected_force; + // If there's no window to paint, and the screen isn't redirected, + // don't redirect it. + if (ps->o.unredir_if_possible && is_highest && !ps->redirected) + unredir_possible = true; if (unredir_possible) { if (ps->redirected) { if (!ps->o.unredir_if_possible_delay || ps->tmout_unredir_hit) @@ -1785,6 +1782,10 @@ paint_all(session_t *ps, XserverRegion region, XserverRegion region_real, win *t for (win *w = t; w; w = w->prev_trans) { // Painting shadow if (w->shadow) { + // Lazy shadow building + if (!paint_isvalid(ps, &w->shadow_paint)) + win_build_shadow(ps, w, 1); + // Shadow is to be painted based on the ignore region of current // window if (w->reg_ignore) { @@ -2050,6 +2051,13 @@ wid_get_prop_wintype(session_t *ps, Window wid) { static void map_win(session_t *ps, Window id) { + // Unmap overlay window if it got mapped but we are currently not + // in redirected state. + if (ps->overlay && id == ps->overlay && !ps->redirected) { + XUnmapWindow(ps->dpy, ps->overlay); + XFlush(ps->dpy); + } + win *w = find_win(ps, id); // Don't care about window mapping if it's an InputOnly window @@ -2831,14 +2839,13 @@ add_win(session_t *ps, Window id, Window prev) { assert(IsViewable == map_state || IsUnmapped == map_state); new->a.map_state = IsUnmapped; - // Get window picture format - if (InputOutput == new->a.class) + if (InputOutput == new->a.class) { + // Get window picture format new->pictfmt = XRenderFindVisualFormat(ps->dpy, new->a.visual); - // Create Damage for window - if (InputOutput == new->a.class) { - set_ignore_next(ps); - new->damage = XDamageCreate(ps->dpy, id, XDamageReportNonEmpty); + // Create Damage for window + set_ignore_next(ps); + new->damage = XDamageCreate(ps->dpy, id, XDamageReportNonEmpty); } calc_win_size(ps, new); @@ -6097,6 +6104,11 @@ init_overlay(session_t *ps) { // Retrieve DamageNotify on root window if we are painting on an // overlay // root_damage = XDamageCreate(ps->dpy, root, XDamageReportNonEmpty); + + // Unmap overlay, firstly. But this typically does not work because + // the window isn't created yet. + // XUnmapWindow(ps->dpy, ps->overlay); + // XFlush(ps->dpy); } else { fprintf(stderr, "Cannot get X Composite overlay window. Falling " @@ -6371,6 +6383,9 @@ tmout_unredir_callback(session_t *ps, timeout_t *tmout) { */ static bool mainloop(session_t *ps) { + // Don't miss timeouts even when we have a LOT of other events! + timeout_run(ps); + // Process existing events // Sometimes poll() returns 1 but no events are actually read, // causing XNextEvent() to block, I have no idea what's wrong, so we @@ -6444,8 +6459,6 @@ mainloop(session_t *ps) { free(ptv); ptv = NULL; - timeout_run(ps); - return true; } @@ -6732,6 +6745,7 @@ session_init(session_t *ps_old, int argc, char **argv) { | ExposureMask | StructureNotifyMask | PropertyChangeMask); + XFlush(ps->dpy); ps->root_width = DisplayWidth(ps->dpy, ps->scr); ps->root_height = DisplayHeight(ps->dpy, ps->scr);