From be3451e09798b1ec72c69e1372e94257219f4abc Mon Sep 17 00:00:00 2001
From: Richard Grenville <pyxlcy@gmail.com>
Date: Mon, 17 Sep 2012 22:15:04 +0800
Subject: Improvement: Defer shadow picture generation

- Defer shadow picture generation to minimize interactions with X,
  hoping to boost performance.

- Fix a rendering issue caused by clip_changed in configure_win().
  Remove clip_changed altogether.

- Split generation of shadow picture from calculating its geometry.

- Cache width/height including borders in struct _win as it's frequently
  used.
---
 compton.c | 167 ++++++++++++++++++++++++++++++++++++--------------------------
 compton.h |  66 ++++++++++++++++++-------
 2 files changed, 145 insertions(+), 88 deletions(-)

diff --git a/compton.c b/compton.c
index 544121ea9..389b857b3 100644
--- a/compton.c
+++ b/compton.c
@@ -30,7 +30,6 @@ Picture cshadow_picture;
 Picture dim_picture = 0;
 Picture root_tile;
 XserverRegion all_damage;
-Bool clip_changed;
 #if HAS_NAME_WINDOW_PIXMAP
 Bool has_name_pixmap;
 #endif
@@ -276,16 +275,6 @@ run_fades(Display *dpy) {
 
     determine_mode(dpy, w);
 
-    if (w->shadow_pict) {
-      XRenderFreePicture(dpy, w->shadow_pict);
-      w->shadow_pict = None;
-
-      free_region(dpy, &w->extents);
-
-      /* rebuild the shadow */
-      w->extents = win_extents(dpy, w);
-    }
-
     /* Must do this last as it might
        destroy f->w in callbacks */
     if (need_dequeue) dequeue_fade(dpy, f);
@@ -577,8 +566,7 @@ make_shadow(Display *dpy, double opacity,
 }
 
 static Picture
-shadow_picture(Display *dpy, double opacity,
-               int width, int height, int *wp, int *hp) {
+shadow_picture(Display *dpy, double opacity, int width, int height) {
   XImage *shadow_image;
   Pixmap shadow_pixmap;
   Picture shadow_picture;
@@ -616,8 +604,6 @@ shadow_picture(Display *dpy, double opacity,
     dpy, shadow_pixmap, gc, shadow_image, 0, 0, 0, 0,
     shadow_image->width, shadow_image->height);
 
-  *wp = shadow_image->width;
-  *hp = shadow_image->height;
   XFreeGC(dpy, gc);
   XDestroyImage(shadow_image);
   XFreePixmap(dpy, shadow_pixmap);
@@ -881,39 +867,24 @@ paint_root(Display *dpy) {
     root_width, root_height);
 }
 
+/**
+ * Get a rectangular region a window (and possibly its shadow) occupies.
+ *
+ * Note w->shadow and shadow geometry must be correct before calling this
+ * function.
+ */
 static XserverRegion
 win_extents(Display *dpy, win *w) {
   XRectangle r;
 
   r.x = w->a.x;
   r.y = w->a.y;
-  r.width = w->a.width + w->a.border_width * 2;
-  r.height = w->a.height + w->a.border_width * 2;
+  r.width = w->widthb;
+  r.height = w->heightb;
 
-  if (win_type_shadow[w->window_type]) {
+  if (w->shadow) {
     XRectangle sr;
 
-    w->shadow_dx = shadow_offset_x;
-    w->shadow_dy = shadow_offset_y;
-
-    if (!w->shadow_pict) {
-      double opacity = shadow_opacity;
-
-      if (w->mode != WINDOW_SOLID) {
-        opacity = opacity * get_opacity_percent(dpy, w);
-      }
-
-      if (HAS_FRAME_OPACITY(w)) {
-        opacity = opacity * frame_opacity;
-      }
-
-      w->shadow_pict = shadow_picture(
-        dpy, opacity,
-        w->a.width + w->a.border_width * 2,
-        w->a.height + w->a.border_width * 2,
-        &w->shadow_width, &w->shadow_height);
-    }
-
     sr.x = w->a.x + w->shadow_dx;
     sr.y = w->a.y + w->shadow_dy;
     sr.width = w->shadow_width;
@@ -953,12 +924,10 @@ border_size(Display *dpy, win *w) {
    * instead of an invalid XID.
    */
 
-  set_ignore(dpy, NextRequest(dpy));
   border = XFixesCreateRegionFromWindow(
     dpy, w->id, WindowRegionBounding);
 
   /* translate this */
-  set_ignore(dpy, NextRequest(dpy));
   XFixesTranslateRegion(dpy, border,
     w->a.x + w->a.border_width,
     w->a.y + w->a.border_width);
@@ -1109,11 +1078,6 @@ paint_all(Display *dpy, XserverRegion region) {
     printf(" %#010lx", w->id);
 #endif
 
-    if (clip_changed) {
-      free_region(dpy, &w->border_size);
-      free_region(dpy, &w->extents);
-    }
-
     if (!w->border_size) {
       w->border_size = border_size(dpy, w);
     }
@@ -1147,6 +1111,28 @@ paint_all(Display *dpy, XserverRegion region) {
       w->frame_opacity_cur = w->frame_opacity;
     }
 
+    // Calculate shadow opacity
+    if (w->frame_opacity)
+      w->shadow_opacity = shadow_opacity * w->frame_opacity;
+    else
+      w->shadow_opacity = shadow_opacity * get_opacity_percent(dpy, w);
+
+    // Rebuild shadow_pict if necessary
+    if (w->flags & WFLAG_SIZE_CHANGE)
+      free_picture(dpy, &w->shadow_pict);
+
+    if (w->shadow
+        && (!w->shadow_pict
+          || w->shadow_opacity != w->shadow_opacity_cur)) {
+      free_picture(dpy, &w->shadow_pict);
+      w->shadow_pict = shadow_picture(dpy, w->shadow_opacity,
+          w->widthb, w->heightb);
+      w->shadow_opacity_cur = w->shadow_opacity;
+    }
+
+    // Reset flags
+    w->flags = 0;
+
     w->prev_trans = t;
     t = w;
   }
@@ -1166,8 +1152,8 @@ paint_all(Display *dpy, XserverRegion region) {
 #if HAS_NAME_WINDOW_PIXMAP
     x = w->a.x;
     y = w->a.y;
-    wid = w->a.width + w->a.border_width * 2;
-    hei = w->a.height + w->a.border_width * 2;
+    wid = w->widthb;
+    hei = w->heightb;
 #else
     x = w->a.x + w->a.border_width;
     y = w->a.y + w->a.border_width;
@@ -1429,6 +1415,19 @@ map_win(Display *dpy, Window id,
   w->a.map_state = IsViewable;
   w->window_type = determine_wintype(dpy, w->id, w->id);
 
+  // Window type change could affect shadow
+  {
+    Bool shadow_old = w->shadow;
+    determine_shadow(dpy, w);
+    if (w->shadow != shadow_old) {
+      calc_shadow_geometry(dpy, w);
+      if (w->extents) {
+        free_region(dpy, &w->extents);
+        w->extents = win_extents(dpy, w);
+      }
+    }
+  }
+
 #ifdef DEBUG_WINTYPE
   printf("map_win(): window %#010lx type %s\n",
     w->id, wintype_name(w->window_type));
@@ -1508,8 +1507,6 @@ finish_unmap_win(Display *dpy, win *w) {
   free_picture(dpy, &w->picture);
   free_region(dpy, &w->border_size);
   free_picture(dpy, &w->shadow_pict);
-
-  clip_changed = True;
 }
 
 #if HAS_NAME_WINDOW_PIXMAP
@@ -1605,16 +1602,6 @@ set_opacity(Display *dpy, win *w, opacity_t opacity) {
 
   w->opacity = opacity;
   determine_mode(dpy, w);
-
-  if (w->shadow_pict) {
-    XRenderFreePicture(dpy, w->shadow_pict);
-    w->shadow_pict = None;
-
-    free_region(dpy, &w->extents);
-
-    /* rebuild the shadow */
-    w->extents = win_extents(dpy, w);
-  }
 }
 
 /**
@@ -1683,6 +1670,37 @@ calc_dim(Display *dpy, win *w) {
   }
 }
 
+/**
+ * Determine if a window should have shadow.
+ */
+static void
+determine_shadow(Display *dpy, win *w) {
+  w->shadow = win_type_shadow[w->window_type];
+}
+
+/**
+ * Update cache data in struct _win that depends on window size.
+ */
+
+static void
+calc_win_size(Display *dpy, win *w) {
+  w->widthb = w->a.width + w->a.border_width * 2;
+  w->heightb = w->a.height + w->a.border_width * 2;
+  calc_shadow_geometry(dpy, w);
+  w->flags |= WFLAG_SIZE_CHANGE;
+}
+
+/**
+ * Calculate and update geometry of the shadow of a window.
+ */
+static void
+calc_shadow_geometry(Display *dpy, win *w) {
+  w->shadow_dx = shadow_offset_x;
+  w->shadow_dy = shadow_offset_y;
+  w->shadow_width = w->widthb + gaussian_map->size;
+  w->shadow_height = w->heightb + gaussian_map->size;
+}
+
 /**
  * Mark a window as the client window of another.
  *
@@ -1749,10 +1767,12 @@ add_win(Display *dpy, Window id, Window prev, Bool override_redirect) {
     new->damage = XDamageCreate(dpy, id, XDamageReportNonEmpty);
   }
 
-  new->shadow = False;
-  new->shadow_pict = None;
   new->border_size = None;
   new->extents = None;
+  new->shadow = False;
+  new->shadow_opacity = 0.0;
+  new->shadow_opacity_cur = 0.0;
+  new->shadow_pict = None;
   new->shadow_dx = 0;
   new->shadow_dy = 0;
   new->shadow_width = 0;
@@ -1779,6 +1799,10 @@ add_win(Display *dpy, Window id, Window prev, Bool override_redirect) {
 
   new->client_win = 0;
 
+  new->flags = 0;
+
+  calc_win_size(dpy, new);
+
   new->next = *p;
   *p = new;
 
@@ -1898,12 +1922,15 @@ configure_win(Display *dpy, XConfigureEvent *ce) {
       free_pixmap(dpy, &w->pixmap);
       free_picture(dpy, &w->picture);
 #endif
-      free_picture(dpy, &w->shadow_pict);
     }
 
-    w->a.width = ce->width;
-    w->a.height = ce->height;
-    w->a.border_width = ce->border_width;
+    if (w->a.width != ce->width || w->a.height != ce->height
+        || w->a.border_width != ce->border_width) {
+      w->a.width = ce->width;
+      w->a.height = ce->height;
+      w->a.border_width = ce->border_width;
+      calc_win_size(dpy, w);
+    }
 
     if (w->a.map_state != IsUnmapped && damage) {
       XserverRegion extents = win_extents(dpy, w);
@@ -1912,7 +1939,9 @@ configure_win(Display *dpy, XConfigureEvent *ce) {
       add_damage(dpy, damage);
     }
 
-    clip_changed = True;
+    // Window extents and border_size may have changed
+    free_region(dpy, &w->extents);
+    free_region(dpy, &w->border_size);
   }
 
   w->a.override_redirect = ce->override_redirect;
@@ -1932,7 +1961,6 @@ circulate_win(Display *dpy, XCirculateEvent *ce) {
   }
 
   restack_win(dpy, w, new_above);
-  clip_changed = True;
 }
 
 static void
@@ -2016,7 +2044,6 @@ damage_win(Display *dpy, XDamageNotifyEvent *de) {
         && w->damage_bounds.y <= 0
         && w->a.width <= w->damage_bounds.x + w->damage_bounds.width
         && w->a.height <= w->damage_bounds.y + w->damage_bounds.height) {
-      clip_changed = True;
       if (win_type_fade[w->window_type]) {
         set_fade(dpy, w, 0, get_opacity_percent(dpy, w),
                  fade_in_step, 0, True, True);
@@ -2935,7 +2962,6 @@ main(int argc, char **argv) {
   }
 
   all_damage = None;
-  clip_changed = True;
   XGrabServer(dpy);
 
   XCompositeRedirectSubwindows(
@@ -2986,7 +3012,6 @@ main(int argc, char **argv) {
       paint++;
       XSync(dpy, False);
       all_damage = None;
-      clip_changed = False;
     }
   }
 }
diff --git a/compton.h b/compton.h
index 3ca318fe8..19f5f2aac 100644
--- a/compton.h
+++ b/compton.h
@@ -47,6 +47,11 @@ extern struct timeval time_start;
 #define WINDOW_TRANS 1
 #define WINDOW_ARGB 2
 
+// Window flags
+
+// Window size is changed
+#define WFLAG_SIZE_CHANGE   0x0001
+
 /**
  * Types
  */
@@ -95,12 +100,17 @@ typedef struct _win {
   Picture picture;
   XserverRegion border_size;
   XserverRegion extents;
-  Bool shadow;
-  Picture shadow_pict;
-  int shadow_dx;
-  int shadow_dy;
-  int shadow_width;
-  int shadow_height;
+  // Type of the window.
+  wintype window_type;
+  /// Whether the window is focused.
+  Bool focused;
+  Bool destroyed;
+  /// Cached width/height of the window including border.
+  int widthb, heightb;
+  unsigned int left_width;
+  unsigned int right_width;
+  unsigned int top_width;
+  unsigned int bottom_width;
 
   /// Current window opacity.
   opacity_t opacity;
@@ -111,24 +121,38 @@ typedef struct _win {
   /// Alpha mask Picture to render window with opacity.
   Picture alpha_pict;
 
-  /// Current window frame opacity.
+  /// Current window frame opacity. Affected by window opacity.
   double frame_opacity;
   /// Opacity of current frame_alpha_pict.
   opacity_t frame_opacity_cur;
   /// Alpha mask Picture to render window frame with opacity.
   Picture frame_alpha_pict;
 
+  /// Whether a window has shadow. Affected by window type.
+  Bool shadow;
+  /// Opacity of the shadow. Affected by window opacity and frame opacity.
+  double shadow_opacity;
+  /// Opacity of current shadow_pict.
+  double shadow_opacity_cur;
+  /// X offset of shadow. Affected by commandline argument.
+  int shadow_dx;
+  /// Y offset of shadow. Affected by commandline argument.
+  int shadow_dy;
+  /// Width of shadow. Affected by window size and commandline argument.
+  int shadow_width;
+  /// Height of shadow. Affected by window size and commandline argument.
+  int shadow_height;
+  /// Alpha mask Picture to render shadow. Affected by window size and
+  /// shadow opacity.
+  Picture shadow_pict;
+
   /// Whether the window is to be dimmed.
   Bool dim;
-  wintype window_type;
-  /// Whether the window is focused.
-  Bool focused;
+
+  /// Window flags. Definitions above.
+  int_fast16_t flags;
+
   unsigned long damage_sequence; /* sequence when damage was created */
-  Bool destroyed;
-  unsigned int left_width;
-  unsigned int right_width;
-  unsigned int top_width;
-  unsigned int bottom_width;
 
   Bool need_configure;
   XConfigureEvent queue_configure;
@@ -409,8 +433,7 @@ make_shadow(Display *dpy, double opacity,
             int width, int height);
 
 static Picture
-shadow_picture(Display *dpy, double opacity,
-               int width, int height, int *wp, int *hp);
+shadow_picture(Display *dpy, double opacity, int width, int height);
 
 static Picture
 solid_picture(Display *dpy, Bool argb, double a,
@@ -502,6 +525,15 @@ calc_opacity(Display *dpy, win *w, Bool refetch_prop);
 static void
 calc_dim(Display *dpy, win *w);
 
+static void
+determine_shadow(Display *dpy, win *w);
+
+static void
+calc_win_size(Display *dpy, win *w);
+
+static void
+calc_shadow_geometry(Display *dpy, win *w);
+
 static void
 mark_client_win(Display *dpy, win *w, Window client);
 
-- 
cgit v1.2.3

