From f9f1e1f228ec21be08833f6aa86fe6ea2c64b625 Mon Sep 17 00:00:00 2001
From: Richard Grenville <pyxlcy@gmail.com>
Date: Fri, 15 Mar 2013 23:16:23 +0800
Subject: Feature: OpenGL backend

- Add experimental OpenGL backend (--opengl). --blur-background is
  currently not possible with this backend, because I'm still trying to
  find a proper way to do blur with OpenGL. Flipping backend on-the-fly
  is really hard, so it isn't supported right now. No configuration file
  option exists to enable this, because it isn't stable enough.

- Add `opengl-swc` VSync method that uses SGI_swap_control to control
  buffer swap, with OpenGL backend. (#7)

- Fix a potential read-from-freed-memory issue in paint_all().

- Correctly reattach GLX context after fork.

- Dump error text in error(). Add GLX error code handling.

- Code clean-up.

- Known issues: Region operations take a lot of time in glx_render().
  I'm hesitating about what to do.
---
 common.h  | 174 +++++++++++++--
 compton.c | 712 ++++++++++++++++++++++++++++++++------------------------------
 compton.h | 137 +++++++-----
 opengl.c  | 521 +++++++++++++++++++++++++++++++++++++++++++++
 opengl.h  |  46 ++++
 5 files changed, 1169 insertions(+), 421 deletions(-)
 create mode 100644 opengl.c
 create mode 100644 opengl.h

diff --git a/common.h b/common.h
index 96ca6021c..30c09e3a5 100644
--- a/common.h
+++ b/common.h
@@ -130,6 +130,9 @@
 #error libXcomposite version unsupported
 #endif
 
+/// @brief Length of generic buffers.
+#define BUF_LEN 80
+
 #define ROUNDED_PERCENT 0.05
 #define ROUNDED_PIXELS  10
 
@@ -152,6 +155,9 @@
 #define XRFILTER_GUASSIAN     "gaussian"
 #define XRFILTER_BINOMIAL     "binomial"
 
+/// @brief Maximum OpenGL FBConfig depth.
+#define OPENGL_MAX_DEPTH 32
+
 // Window flags
 
 // Window size is changed
@@ -249,9 +255,18 @@ typedef enum {
   VSYNC_DRM,
   VSYNC_OPENGL,
   VSYNC_OPENGL_OML,
+  VSYNC_OPENGL_SWC,
   NUM_VSYNC,
 } vsync_t;
 
+/// @brief Possible backends of compton.
+enum backend {
+  BKEND_XRENDER,
+  BKEND_GLX,
+};
+
+typedef struct _glx_texture glx_texture_t;
+
 #ifdef CONFIG_VSYNC_OPENGL
 typedef int (*f_WaitVideoSync) (int, int, unsigned *);
 typedef int (*f_GetVideoSync) (unsigned *);
@@ -259,15 +274,39 @@ typedef int (*f_GetVideoSync) (unsigned *);
 typedef Bool (*f_GetSyncValuesOML) (Display* dpy, GLXDrawable drawable, int64_t* ust, int64_t* msc, int64_t* sbc);
 typedef Bool (*f_WaitForMscOML) (Display* dpy, GLXDrawable drawable, int64_t target_msc, int64_t divisor, int64_t remainder, int64_t* ust, int64_t* msc, int64_t* sbc);
 
+typedef int (*f_SwapIntervalSGI) (int interval);
+
 typedef void (*f_BindTexImageEXT) (Display *display, GLXDrawable drawable, int buffer, const int *attrib_list);
 typedef void (*f_ReleaseTexImageEXT) (Display *display, GLXDrawable drawable, int buffer);
 
-struct glx_fbconfig {
+/// @brief Wrapper of a GLX FBConfig.
+typedef struct {
   GLXFBConfig cfg;
+  GLint texture_fmt;
+  GLint texture_tgts;
+  bool y_inverted;
+} glx_fbconfig_t;
+
+/// @brief Wrapper of a binded GLX texture.
+struct _glx_texture {
+  GLuint texture;
+  GLXPixmap glpixmap;
+  Pixmap pixmap;
+  int width;
+  int height;
+  int depth;
   bool y_inverted;
 };
 #endif
 
+typedef struct {
+  Pixmap pixmap;
+  Picture pict;
+  glx_texture_t *ptex;
+} paint_t;
+
+#define PAINT_INIT { .pixmap = None, .pict = None }
+
 typedef struct {
   int size;
   double *data;
@@ -293,6 +332,8 @@ typedef struct {
   /// The display name we used. NULL means we are using the value of the
   /// <code>DISPLAY</code> environment variable.
   char *display;
+  /// The backend in use.
+  enum backend backend;
   /// Whether to try to detect WM windows and mark them as focused.
   bool mark_wmwin_focused;
   /// Whether to mark override-redirect windows as focused.
@@ -434,8 +475,10 @@ typedef struct {
   // Damage root_damage;
   /// X Composite overlay window. Used if <code>--paint-on-overlay</code>.
   Window overlay;
+  /// Whether the root tile is filled by compton.
+  bool root_tile_fill;
   /// Picture of the root window background.
-  Picture root_tile;
+  paint_t root_tile_paint;
   /// A region of the size of the screen.
   XserverRegion screen_reg;
   /// Picture of root window. Destination of painting in no-DBE painting
@@ -488,6 +531,10 @@ typedef struct {
   /// Pointer to the <code>next</code> member of tail element of the error
   /// ignore linked list.
   ignore_t **ignore_tail;
+#ifdef CONFIG_VSYNC_OPENGL
+  /// Current GLX Z value.
+  int glx_z;
+#endif
   /// Reset program after next paint.
   bool reset;
 
@@ -551,18 +598,18 @@ typedef struct {
   f_GetVideoSync glXGetVideoSyncSGI;
   /// Pointer to glXWaitVideoSyncSGI function.
   f_WaitVideoSync glXWaitVideoSyncSGI;
-  /// Pointer to glXGetSyncValuesOML function.
+   /// Pointer to glXGetSyncValuesOML function.
   f_GetSyncValuesOML glXGetSyncValuesOML;
   /// Pointer to glXWaitForMscOML function.
   f_WaitForMscOML glXWaitForMscOML;
+  /// Pointer to glXSwapIntervalSGI function.
+  f_SwapIntervalSGI glXSwapIntervalProc;
   /// Pointer to glXBindTexImageEXT function.
-  f_BindTexImageEXT glXBindTexImageEXT;
+  f_BindTexImageEXT glXBindTexImageProc;
   /// Pointer to glXReleaseTexImageEXT function.
-  f_ReleaseTexImageEXT glXReleaseTexImageEXT;
-  /// FBConfig for RGB GLX pixmap.
-  struct glx_fbconfig *glx_fbconfig_rgb;
-  /// FBConfig for RGBA GLX pixmap.
-  struct glx_fbconfig *glx_fbconfig_rgba;
+  f_ReleaseTexImageEXT glXReleaseTexImageProc;
+  /// FBConfig-s for GLX pixmap of different depths.
+  glx_fbconfig_t *glx_fbconfigs[OPENGL_MAX_DEPTH + 1];
 #endif
 
   // === X extension related ===
@@ -664,16 +711,16 @@ typedef struct _win {
   Window id;
   /// Window attributes.
   XWindowAttributes a;
+  /// Window visual pict format;
+  XRenderPictFormat *pictfmt;
   /// Window painting mode.
   winmode_t mode;
   /// Whether the window has been damaged at least once.
   bool damaged;
   /// Damage of the window.
   Damage damage;
-  /// NameWindowPixmap of the window.
-  Pixmap pixmap;
-  /// Picture of the window.
-  Picture picture;
+  /// Paint info of the window.
+  paint_t paint;
   /// Bounding shape of the window.
   XserverRegion border_size;
   /// Region of the whole window, shadow region included.
@@ -750,8 +797,6 @@ typedef struct _win {
   /// broken window managers not transferring client window's
   /// _NET_WM_OPACITY value
   opacity_t opacity_prop_client;
-  /// Alpha mask Picture to render window with opacity.
-  Picture alpha_pict;
 
   // Fading-related members
   /// Do not fade if it's false. Change on window type change.
@@ -763,8 +808,6 @@ typedef struct _win {
   // Frame-opacity-related members
   /// Current window frame opacity. Affected by window opacity.
   double frame_opacity;
-  /// Alpha mask Picture to render window frame with opacity.
-  Picture frame_alpha_pict;
   /// Frame widths. Determined by client window attributes.
   unsigned int left_width, right_width, top_width, bottom_width;
 
@@ -784,9 +827,7 @@ typedef struct _win {
   /// Height of shadow. Affected by window size and commandline argument.
   int shadow_height;
   /// Picture to render shadow. Affected by window size.
-  Picture shadow_pict;
-  /// Alpha mask Picture to render shadow. Affected by shadow opacity.
-  Picture shadow_alpha_pict;
+  paint_t shadow_paint;
   /// The value of _COMPTON_SHADOW attribute of the window. Below 0 for
   /// none.
   long prop_shadow;
@@ -896,6 +937,20 @@ XFixesDestroyRegion_(Display *dpy, XserverRegion reg,
 
 // === Functions ===
 
+/**
+ * @brief Quit if the passed-in pointer is empty.
+ */
+static inline void
+allocchk_(const char *func_name, void *ptr) {
+  if (!ptr) {
+    printf_err("%s(): Failed to allocate memory.", func_name);
+    exit(1);
+  }
+}
+
+/// @brief Wrapper of allocchk_().
+#define allocchk(ptr) allocchk_(__func__, ptr)
+
 /**
  * Return whether a struct timeval value is empty.
  */
@@ -1304,6 +1359,14 @@ get_atom(session_t *ps, const char *atom_name) {
   return XInternAtom(ps->dpy, atom_name, False);
 }
 
+/**
+ * Return the painting target window.
+ */
+static inline Window
+get_tgt_window(session_t *ps) {
+  return ps->o.paint_on_overlay ? ps->overlay: ps->root;
+}
+
 /**
  * Find a window from window id in window linked list of the session.
  */
@@ -1371,6 +1434,17 @@ copy_region(const session_t *ps, XserverRegion oldregion) {
   return region;
 }
 
+/**
+ * Destroy a <code>XserverRegion</code>.
+ */
+static inline void
+free_region(session_t *ps, XserverRegion *p) {
+  if (*p) {
+    XFixesDestroyRegion(ps->dpy, *p);
+    *p = None;
+  }
+}
+
 /**
  * Determine if a window has a specific property.
  *
@@ -1454,6 +1528,66 @@ force_repaint(session_t *ps);
 bool
 vsync_init(session_t *ps);
 
+#ifdef CONFIG_VSYNC_OPENGL
+/** @name GLX
+ */
+///@{
+
+bool
+glx_init(session_t *ps, bool need_render);
+
+void
+glx_destroy(session_t *ps);
+
+void
+glx_on_root_change(session_t *ps);
+
+bool
+glx_bind_pixmap(session_t *ps, glx_texture_t **pptex, Pixmap pixmap,
+    int width, int height, int depth);
+
+void
+glx_release_pixmap(session_t *ps, glx_texture_t *ptex);
+
+static inline bool
+glx_tex_binded(const glx_texture_t *ptex) {
+  return ptex && ptex->glpixmap && ptex->texture;
+}
+
+bool
+glx_render(session_t *ps, const glx_texture_t *ptex,
+    int x, int y, int dx, int dy, int width, int height, int z,
+    double opacity, bool neg, XserverRegion reg_tgt);
+#endif
+
+static inline void
+free_texture(session_t *ps, glx_texture_t **pptex) {
+#ifdef CONFIG_VSYNC_OPENGL
+  glx_texture_t *ptex = *pptex;
+
+  // Quit if there's nothing
+  if (!ptex)
+    return;
+
+  glx_release_pixmap(ps, ptex);
+
+  // Free texture
+  if (ptex->texture) {
+    glDeleteTextures(1, &ptex->texture);
+    ptex->texture = 0;
+  }
+
+  // Free structure itself
+  free(ptex);
+  *pptex = NULL;
+#endif
+}
+
+///@}
+
+/** @name DBus handling
+ */
+///@{
 #ifdef CONFIG_DBUS
 /** @name DBus handling
  */
diff --git a/compton.c b/compton.c
index 58a716c50..f8c33d265 100644
--- a/compton.c
+++ b/compton.c
@@ -37,6 +37,7 @@ const char * const VSYNC_STRS[NUM_VSYNC] = {
   "drm",          // VSYNC_DRM
   "opengl",       // VSYNC_OPENGL
   "opengl-oml",   // VSYNC_OPENGL_OML
+  "opengl-swc",   // VSYNC_OPENGL_SWC
 };
 
 /// Function pointers to init VSync modes.
@@ -44,6 +45,7 @@ static bool (* const (VSYNC_FUNCS_INIT[NUM_VSYNC]))(session_t *ps) = {
   [VSYNC_DRM        ] = vsync_drm_init,
   [VSYNC_OPENGL     ] = vsync_opengl_init,
   [VSYNC_OPENGL_OML ] = vsync_opengl_oml_init,
+  [VSYNC_OPENGL_SWC ] = vsync_opengl_swc_init,
 };
 
 /// Function pointers to wait for VSync.
@@ -425,8 +427,11 @@ make_shadow(session_t *ps, double opacity,
 /**
  * Generate shadow <code>Picture</code> for a window.
  */
-static Picture
-shadow_picture(session_t *ps, double opacity, int width, int height) {
+static bool
+win_build_shadow(session_t *ps, win *w, double opacity) {
+  const int width = w->widthb;
+  const int height = w->heightb;
+
   XImage *shadow_image = NULL;
   Pixmap shadow_pixmap = None, shadow_pixmap_argb = None;
   Picture shadow_picture = None, shadow_picture_argb = None;
@@ -461,13 +466,16 @@ shadow_picture(session_t *ps, double opacity, int width, int height) {
       shadow_picture_argb, 0, 0, 0, 0, 0, 0,
       shadow_image->width, shadow_image->height);
 
+  w->shadow_paint.pixmap = shadow_pixmap_argb;
+  w->shadow_paint.pict = shadow_picture_argb;
+  bool success = paint_bind_tex(ps, &w->shadow_paint, shadow_image->width, shadow_image->height, 32);
+
   XFreeGC(ps->dpy, gc);
   XDestroyImage(shadow_image);
   XFreePixmap(ps->dpy, shadow_pixmap);
-  XFreePixmap(ps->dpy, shadow_pixmap_argb);
   XRenderFreePicture(ps->dpy, shadow_picture);
 
-  return shadow_picture_argb;
+  return success;
 
 shadow_picture_err:
   if (shadow_image)
@@ -482,7 +490,8 @@ shadow_picture_err:
     XRenderFreePicture(ps->dpy, shadow_picture_argb);
   if (gc)
     XFreeGC(ps->dpy, gc);
-  return None;
+
+  return false;
 }
 
 /**
@@ -653,7 +662,7 @@ win_rounded_corners(session_t *ps, win *w) {
  */
 static void
 win_validate_pixmap(session_t *ps, win *w) {
-  if (!w->pixmap)
+  if (!w->paint.pixmap)
     return;
 
   // Detect whether the pixmap is valid with XGetGeometry. Well, maybe there
@@ -663,15 +672,13 @@ win_validate_pixmap(session_t *ps, win *w) {
     Window rroot = None;
     int rx = 0, ry = 0;
     unsigned rwid = 0, rhei = 0, rborder = 0, rdepth = 0;
-    invalid = (!XGetGeometry(ps->dpy, w->pixmap, &rroot, &rx, &ry,
+    invalid = (!XGetGeometry(ps->dpy, w->paint.pixmap, &rroot, &rx, &ry,
           &rwid, &rhei, &rborder, &rdepth) || !rwid || !rhei);
   }
 
   // Destroy pixmap and picture, if invalid
-  if (invalid) {
-    free_pixmap(ps, &w->pixmap);
-    free_picture(ps, &w->picture);
-  }
+  if (invalid)
+    free_paint(ps, &w->paint);
 }
 
 /**
@@ -794,23 +801,21 @@ recheck_focus(session_t *ps) {
   return NULL;
 }
 
-static Picture
-root_tile_f(session_t *ps) {
+static bool
+get_root_tile(session_t *ps) {
   /*
   if (ps->o.paint_on_overlay) {
     return ps->root_picture;
   } */
 
-  Picture picture;
-  Pixmap pixmap;
-  bool fill = false;
-  XRenderPictureAttributes pa;
-  int p;
+  assert(!ps->root_tile_paint.pixmap);
+  ps->root_tile_fill = false;
 
-  pixmap = None;
+  bool fill = false;
+  Pixmap pixmap = None;
 
   // Get the values of background attributes
-  for (p = 0; background_props_str[p]; p++) {
+  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, XA_PIXMAP, 32);
@@ -823,43 +828,52 @@ root_tile_f(session_t *ps) {
     free_winprop(&prop);
   }
 
+  // Create a pixmap if there isn't any
   if (!pixmap) {
     pixmap = XCreatePixmap(ps->dpy, ps->root, 1, 1, ps->depth);
     fill = true;
   }
 
-  pa.repeat = True;
-  picture = XRenderCreatePicture(
-    ps->dpy, pixmap, XRenderFindVisualFormat(ps->dpy, ps->vis),
-    CPRepeat, &pa);
+  // Create Picture
+  {
+    XRenderPictureAttributes pa = {
+      .repeat = True,
+    };
+    ps->root_tile_paint.pict = XRenderCreatePicture(
+        ps->dpy, pixmap, XRenderFindVisualFormat(ps->dpy, ps->vis),
+        CPRepeat, &pa);
+  }
 
+  // Fill pixmap if needed
   if (fill) {
     XRenderColor  c;
 
     c.red = c.green = c.blue = 0x8080;
     c.alpha = 0xffff;
-    XRenderFillRectangle(
-      ps->dpy, PictOpSrc, picture, &c, 0, 0, 1, 1);
-
-    free_pixmap(ps, &pixmap);
+    XRenderFillRectangle(ps->dpy, PictOpSrc, ps->root_tile_paint.pict, &c, 0, 0, 1, 1);
   }
 
-  return picture;
+  ps->root_tile_fill = fill;
+  ps->root_tile_paint.pixmap = pixmap;
+#ifdef CONFIG_VSYNC_OPENGL
+  if (BKEND_GLX == ps->o.backend)
+    return glx_bind_pixmap(ps, &ps->root_tile_paint.ptex, ps->root_tile_paint.pixmap,
+        ps->root_width, ps->root_height, ps->depth);
+#endif
+
+  return true;
 }
 
 /**
  * Paint root window content.
  */
 static void
-paint_root(session_t *ps, Picture tgt_buffer) {
-  if (!ps->root_tile) {
-    ps->root_tile = root_tile_f(ps);
-  }
+paint_root(session_t *ps, XserverRegion reg_paint) {
+  if (!ps->root_tile_paint.pixmap)
+    get_root_tile(ps);
 
-  XRenderComposite(
-    ps->dpy, PictOpSrc, ps->root_tile, None,
-    tgt_buffer, 0, 0, 0, 0, 0, 0,
-    ps->root_width, ps->root_height);
+  win_render(ps, NULL, 0, 0, ps->root_width, ps->root_height, 1.0, reg_paint,
+      ps->root_tile_paint.pict);
 }
 
 /**
@@ -1106,12 +1120,12 @@ paint_preprocess(session_t *ps, win *list) {
     run_fade(ps, w, steps);
 
     // Give up if it's not damaged or invisible, or it's unmapped and its
-    // picture is gone (for example due to a ConfigureNotify)
+    // pixmap is gone (for example due to a ConfigureNotify)
     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->picture)) {
+          && !w->paint.pixmap)) {
       to_paint = false;
     }
 
@@ -1122,12 +1136,8 @@ paint_preprocess(session_t *ps, win *list) {
         add_damage_win(ps, w);
       }
 
-      w->alpha_pict = get_alpha_pict_o(ps, w->opacity);
-
-      // End the game if we are using the 0 opacity alpha_pict
-      if (w->alpha_pict == ps->alpha_picts[0]) {
+      if (get_alpha_pict_o(ps, w->opacity) == ps->alpha_picts[0])
         to_paint = false;
-      }
     }
 
     if (to_paint) {
@@ -1161,23 +1171,19 @@ paint_preprocess(session_t *ps, win *list) {
           ps->reg_ignore_expire = true;
       }
 
-      w->frame_alpha_pict = get_alpha_pict_d(ps, w->frame_opacity);
-
       // Calculate shadow opacity
       if (w->frame_opacity)
         w->shadow_opacity = ps->o.shadow_opacity * w->frame_opacity;
       else
         w->shadow_opacity = ps->o.shadow_opacity * get_opacity_percent(w);
 
-      // Rebuild shadow_pict if necessary
-      if (w->flags & WFLAG_SIZE_CHANGE)
-        free_picture(ps, &w->shadow_pict);
-
-      if (w->shadow && !w->shadow_pict) {
-        w->shadow_pict = shadow_picture(ps, 1, w->widthb, w->heightb);
+      // Rebuild shadow if necessary
+      if (w->flags & WFLAG_SIZE_CHANGE) {
+        free_paint(ps, &w->shadow_paint);
       }
 
-      w->shadow_alpha_pict = get_alpha_pict_d(ps, w->shadow_opacity);
+      if (w->shadow && !paint_isvalid(ps, &w->shadow_paint))
+        win_build_shadow(ps, w, 1);
 
       // Dimming preprocessing
       if (w->dim) {
@@ -1272,22 +1278,29 @@ paint_preprocess(session_t *ps, win *list) {
   // Fetch pictures only if windows are redirected
   if (ps->redirected) {
     for (w = t; w; w = w->prev_trans) {
-      // Fetch the picture and pixmap if needed
-      if (!w->picture) {
-        XRenderPictureAttributes pa;
-        XRenderPictFormat *format;
-        Drawable draw = w->id;
-
-        if (ps->has_name_pixmap && !w->pixmap) {
+      // Fetch Pixmap
+      if (!w->paint.pixmap && ps->has_name_pixmap) {
           set_ignore_next(ps);
-          w->pixmap = XCompositeNameWindowPixmap(ps->dpy, w->id);
+          w->paint.pixmap = XCompositeNameWindowPixmap(ps->dpy, w->id);
+      }
+      // XRender: Build picture
+      if (BKEND_XRENDER == ps->o.backend && !w->paint.pict) {
+        Drawable draw = w->paint.pixmap;
+        if (!draw)
+          draw = w->id;
+        {
+          XRenderPictureAttributes pa = {
+            .subwindow_mode = IncludeInferiors,
+          };
+
+          w->paint.pict = XRenderCreatePicture(ps->dpy, draw, w->pictfmt,
+              CPSubwindowMode, &pa);
         }
-        if (w->pixmap) draw = w->pixmap;
-
-        format = XRenderFindVisualFormat(ps->dpy, w->a.visual);
-        pa.subwindow_mode = IncludeInferiors;
-        w->picture = XRenderCreatePicture(
-          ps->dpy, draw, format, CPSubwindowMode, &pa);
+      }
+      // OpenGL: Build texture
+      if (!paint_bind_tex(ps, &w->paint, w->widthb, w->heightb,
+            w->pictfmt->depth)) {
+        printf_errf("(%#010lx): Failed to bind texture. Expect troubles.", w->id);
       }
     }
   }
@@ -1299,41 +1312,36 @@ paint_preprocess(session_t *ps, win *list) {
  * Paint the shadow of a window.
  */
 static inline void
-win_paint_shadow(session_t *ps, win *w, Picture tgt_buffer) {
-  XRenderComposite(
-    ps->dpy, PictOpOver, w->shadow_pict, w->shadow_alpha_pict,
-    tgt_buffer, 0, 0, 0, 0,
-    w->a.x + w->shadow_dx, w->a.y + w->shadow_dy,
-    w->shadow_width, w->shadow_height);
+win_paint_shadow(session_t *ps, win *w, XserverRegion reg_paint) {
+  if (!paint_isvalid(ps, &w->shadow_paint)) {
+    printf_errf("(%#010lx): Missing painting data. This is a bad sign.", w->id);
+    return;
+  }
+
+  render(ps, 0, 0, w->a.x + w->shadow_dx, w->a.y + w->shadow_dy,
+      w->shadow_width, w->shadow_height, w->shadow_opacity, true, false,
+      w->shadow_paint.pict, w->shadow_paint.ptex, reg_paint);
 }
 
 /**
  * Create an alternative picture for a window.
  */
 static inline Picture
-win_build_picture(session_t *ps, win *w, Visual *visual) {
+win_build_picture(session_t *ps, win *w, XRenderPictFormat *pictfmt) {
   const int wid = w->widthb;
   const int hei = w->heightb;
-  int depth = 0;
-  XRenderPictFormat *pictformat = NULL;
 
-  if (visual && ps->vis != visual) {
-    pictformat = XRenderFindVisualFormat(ps->dpy, visual);
-    depth = pictformat->depth;
-  }
-  else {
-    pictformat = XRenderFindVisualFormat(ps->dpy, ps->vis);
-  }
+  if (!pictfmt)
+    pictfmt = XRenderFindVisualFormat(ps->dpy, ps->vis);
 
-  if (!depth)
-    depth = ps->depth;
+  int depth = pictfmt->depth;
 
   Pixmap tmp_pixmap = XCreatePixmap(ps->dpy, ps->root, wid, hei, depth);
   if (!tmp_pixmap)
     return None;
 
   Picture tmp_picture = XRenderCreatePicture(ps->dpy, tmp_pixmap,
-    pictformat, 0, 0);
+    pictfmt, 0, 0);
   free_pixmap(ps, &tmp_pixmap);
 
   return tmp_picture;
@@ -1401,23 +1409,53 @@ win_blur_background(session_t *ps, win *w, Picture tgt_buffer,
   free_picture(ps, &tmp_picture);
 }
 
+static void
+render(session_t *ps, int x, int y, int dx, int dy, int wid, int hei,
+    double opacity, bool argb, bool neg,
+    Picture pict, glx_texture_t *ptex, XserverRegion reg_paint) {
+  switch (ps->o.backend) {
+    case BKEND_XRENDER:
+      {
+        Picture alpha_pict = get_alpha_pict_d(ps, opacity);
+        if (alpha_pict != ps->alpha_picts[0]) {
+          int op = ((!argb && !alpha_pict) ? PictOpSrc: PictOpOver);
+          XRenderComposite(ps->dpy, op, pict, alpha_pict,
+              ps->tgt_buffer, x, y, 0, 0, dx, dy, wid, hei);
+        }
+        break;
+      }
+#ifdef CONFIG_VSYNC_OPENGL
+    case BKEND_GLX:
+      glx_render(ps, ptex, x, y, dx, dy, wid, hei,
+          ps->glx_z, opacity, neg, reg_paint);
+      ps->glx_z += 1;
+      break;
+#endif
+    default:
+      assert(0);
+  }
+}
+
 /**
  * Paint a window itself and dim it if asked.
  */
 static inline void
-win_paint_win(session_t *ps, win *w, Picture tgt_buffer, XserverRegion reg_paint) {
-  int x = w->a.x;
-  int y = w->a.y;
-  int wid = w->widthb;
-  int hei = w->heightb;
+win_paint_win(session_t *ps, win *w, XserverRegion reg_paint) {
+  if (!paint_isvalid(ps, &w->paint)) {
+    printf_errf("(%#010lx): Missing painting data. This is a bad sign.", w->id);
+    return;
+  }
+
+  const int x = w->a.x;
+  const int y = w->a.y;
+  const int wid = w->widthb;
+  const int hei = w->heightb;
 
-  Picture alpha_mask = (OPAQUE == w->opacity ? None: w->alpha_pict);
-  int op = (w->mode == WMODE_SOLID ? PictOpSrc: PictOpOver);
-  Picture pict = w->picture;
+  Picture pict = w->paint.pict;
 
   // Invert window color, if required
-  if (w->invert_color) {
-    Picture newpict = win_build_picture(ps, w, w->a.visual);
+  if (BKEND_XRENDER == ps->o.backend && w->invert_color) {
+    Picture newpict = win_build_picture(ps, w, w->pictfmt);
     if (newpict) {
       // Apply clipping region to save some CPU
       if (reg_paint) {
@@ -1440,9 +1478,10 @@ win_paint_win(session_t *ps, win *w, Picture tgt_buffer, XserverRegion reg_paint
     }
   }
 
+  double dopacity = get_opacity_percent(w);;
+
   if (!w->frame_opacity) {
-    XRenderComposite(ps->dpy, op, pict, alpha_mask,
-        tgt_buffer, 0, 0, 0, 0, x, y, wid, hei);
+    win_render(ps, w, 0, 0, wid, hei, dopacity, reg_paint, pict);
   }
   else {
     // Painting parameters
@@ -1452,8 +1491,8 @@ win_paint_win(session_t *ps, win *w, Picture tgt_buffer, XserverRegion reg_paint
     const int r = w->a.border_width + w->right_width;
 
 #define COMP_BDR(cx, cy, cwid, chei) \
-    XRenderComposite(ps->dpy, PictOpOver, pict, w->frame_alpha_pict, \
-        tgt_buffer, (cx), (cy), 0, 0, x + (cx), y + (cy), (cwid), (chei))
+    win_render(ps, w, (cx), (cy), (cwid), (chei), w->frame_opacity, \
+        reg_paint, pict)
 
     // The following complicated logic is required because some broken
     // window managers (I'm talking about you, Openbox!) that makes
@@ -1488,8 +1527,7 @@ win_paint_win(session_t *ps, win *w, Picture tgt_buffer, XserverRegion reg_paint
           pwid = wid - l - pwid;
           if (pwid > 0) {
             // body
-            XRenderComposite(ps->dpy, op, pict, alpha_mask,
-                tgt_buffer, l, t, 0, 0, x + l, y + t, pwid, phei);
+            win_render(ps, w, l, t, pwid, phei, dopacity, reg_paint, pict);
           }
         }
       }
@@ -1498,13 +1536,13 @@ win_paint_win(session_t *ps, win *w, Picture tgt_buffer, XserverRegion reg_paint
 
 #undef COMP_BDR
 
-  if (pict != w->picture)
+  if (pict != w->paint.pict)
     free_picture(ps, &pict);
 
   // Dimming the window if needed
   if (w->dim && w->dim_alpha_pict != ps->alpha_picts[0]) {
     XRenderComposite(ps->dpy, PictOpOver, ps->black_picture,
-        w->dim_alpha_pict, tgt_buffer, 0, 0, 0, 0, x, y, wid, hei);
+        w->dim_alpha_pict, ps->tgt_buffer, 0, 0, 0, 0, x, y, wid, hei);
   }
 }
 
@@ -1523,10 +1561,19 @@ paint_all(session_t *ps, XserverRegion region, win *t) {
 #ifdef DEBUG_REPAINT
   static struct timespec last_paint = { 0 };
 #endif
-
-  win *w;
+  win *w = NULL;
   XserverRegion reg_paint = None, reg_tmp = None, reg_tmp2 = None;
 
+#ifdef CONFIG_VSYNC_OPENGL
+  // GLX backend: OpenGL doesn't support partial repaint without
+  // GLX_MESA_copy_sub_buffer
+  if (BKEND_GLX == ps->o.backend) {
+    free_region(ps, &region);
+    // glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+    ps->glx_z = 0.0;
+  }
+#endif
+
   if (!region) {
     region = get_screen_region(ps);
   }
@@ -1583,7 +1630,7 @@ paint_all(session_t *ps, XserverRegion region, win *t) {
 
   XFixesSetPictureClipRegion(ps->dpy, ps->tgt_buffer, 0, 0, reg_paint);
 
-  paint_root(ps, ps->tgt_buffer);
+  paint_root(ps, reg_paint);
 
   // Create temporary regions for use during painting
   if (!reg_tmp)
@@ -1622,7 +1669,7 @@ paint_all(session_t *ps, XserverRegion region, win *t) {
         XFixesSetPictureClipRegion(ps->dpy, ps->tgt_buffer, 0, 0,
             reg_paint);
 
-        win_paint_shadow(ps, w, ps->tgt_buffer);
+        win_paint_shadow(ps, w, reg_paint);
       }
     }
 
@@ -1655,10 +1702,8 @@ paint_all(session_t *ps, XserverRegion region, win *t) {
       }
 
       // Painting the window
-      win_paint_win(ps, w, ps->tgt_buffer, reg_paint);
+      win_paint_win(ps, w, reg_paint);
     }
-
-    check_fade_fin(ps, w);
   }
 
   // Free up all temporary regions
@@ -1688,21 +1733,32 @@ paint_all(session_t *ps, XserverRegion region, win *t) {
   if (!ps->o.vsync_aggressive)
     vsync_wait(ps);
 
-  // DBE painting mode, only need to swap the buffer
-  if (ps->o.dbe) {
-    XdbeSwapInfo swap_info = {
-      .swap_window = get_tgt_window(ps),
-      // Is it safe to use XdbeUndefined?
-      .swap_action = XdbeCopied
-    };
-    XdbeSwapBuffers(ps->dpy, &swap_info, 1);
-  }
-  // No-DBE painting mode
-  else if (ps->tgt_buffer != ps->tgt_picture) {
-    XRenderComposite(
-      ps->dpy, PictOpSrc, ps->tgt_buffer, None,
-      ps->tgt_picture, 0, 0, 0, 0,
-      0, 0, ps->root_width, ps->root_height);
+  switch (ps->o.backend) {
+    case BKEND_XRENDER:
+      // DBE painting mode, only need to swap the buffer
+      if (ps->o.dbe) {
+        XdbeSwapInfo swap_info = {
+          .swap_window = get_tgt_window(ps),
+          // Is it safe to use XdbeUndefined?
+          .swap_action = XdbeCopied
+        };
+        XdbeSwapBuffers(ps->dpy, &swap_info, 1);
+      }
+      // No-DBE painting mode
+      else if (ps->tgt_buffer != ps->tgt_picture) {
+        XRenderComposite(
+          ps->dpy, PictOpSrc, ps->tgt_buffer, None,
+          ps->tgt_picture, 0, 0, 0, 0,
+          0, 0, ps->root_width, ps->root_height);
+      }
+      break;
+#ifdef CONFIG_VSYNC_OPENGL
+    case BKEND_GLX:
+      glXSwapBuffers(ps->dpy, get_tgt_window(ps));
+      break;
+#endif
+    default:
+      assert(0);
   }
 
   if (ps->o.vsync_aggressive)
@@ -1730,6 +1786,15 @@ paint_all(session_t *ps, XserverRegion region, win *t) {
   putchar('\n');
   fflush(stdout);
 #endif
+
+  // Check if fading is finished on all painted windows
+  {
+    win *pprev = NULL;
+    for (w = t; w; w = pprev) {
+      pprev = w->prev_trans;
+      check_fade_fin(ps, w);
+    }
+  }
 }
 
 static void
@@ -1822,7 +1887,7 @@ map_win(session_t *ps, Window id) {
   // Make sure the XSelectInput() requests are sent
   XSync(ps->dpy, False);
 
-  // Update window mode here to do matching of ARGB
+  // Update window mode here to check for ARGB windows
   win_determine_mode(ps, w);
 
   // Detect client window here instead of in add_win() as the client
@@ -1917,10 +1982,9 @@ finish_unmap_win(session_t *ps, win *w) {
     w->extents = None;
   }
 
-  free_pixmap(ps, &w->pixmap);
-  free_picture(ps, &w->picture);
+  free_paint(ps, &w->paint);
   free_region(ps, &w->border_size);
-  free_picture(ps, &w->shadow_pict);
+  free_paint(ps, &w->shadow_paint);
 }
 
 static void
@@ -1983,16 +2047,9 @@ get_opacity_percent(win *w) {
 static void
 win_determine_mode(session_t *ps, win *w) {
   winmode_t mode = WMODE_SOLID;
-  XRenderPictFormat *format;
-
-  if (w->a.class == InputOnly) {
-    format = 0;
-  } else {
-    format = XRenderFindVisualFormat(ps->dpy, w->a.visual);
-  }
 
-  if (format && format->type == PictTypeDirect
-      && format->direct.alphaMask) {
+  if (w->pictfmt && w->pictfmt->type == PictTypeDirect
+      && w->pictfmt->direct.alphaMask) {
     mode = WMODE_ARGB;
   } else if (w->opacity != OPAQUE) {
     mode = WMODE_TRANS;
@@ -2107,7 +2164,7 @@ win_update_shape(session_t *ps, win *w) {
     // If clear_shadow state on the window possibly changed, destroy the old
     // shadow_pict
     if (ps->o.clear_shadow && w->bounding_shaped != bounding_shaped_old)
-      free_picture(ps, &w->shadow_pict);
+      free_paint(ps, &w->shadow_paint);
     */
   }
 }
@@ -2404,11 +2461,11 @@ add_win(session_t *ps, Window id, Window prev) {
 
     .id = None,
     .a = { },
+    .pictfmt = NULL,
     .mode = WMODE_TRANS,
     .damaged = false,
     .damage = None,
-    .pixmap = None,
-    .picture = None,
+    .paint = PAINT_INIT,
     .border_size = None,
     .extents = None,
     .flags = 0,
@@ -2446,13 +2503,11 @@ add_win(session_t *ps, Window id, Window prev) {
     .opacity_tgt = 0,
     .opacity_prop = OPAQUE,
     .opacity_prop_client = OPAQUE,
-    .alpha_pict = None,
 
     .fade = false,
     .fade_callback = NULL,
 
     .frame_opacity = 0.0,
-    .frame_alpha_pict = None,
     .left_width = 0,
     .right_width = 0,
     .top_width = 0,
@@ -2465,8 +2520,7 @@ add_win(session_t *ps, Window id, Window prev) {
     .shadow_dy = 0,
     .shadow_width = 0,
     .shadow_height = 0,
-    .shadow_pict = None,
-    .shadow_alpha_pict = None,
+    .shadow_paint = PAINT_INIT,
     .prop_shadow = -1,
 
     .dim = false,
@@ -2520,6 +2574,10 @@ 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)
+    new->pictfmt = XRenderFindVisualFormat(ps->dpy, new->a.visual);
+
   // Create Damage for window
   if (InputOutput == new->a.class) {
     set_ignore_next(ps);
@@ -2608,6 +2666,7 @@ restack_win(session_t *ps, win *w, Window new_above) {
 
 static void
 configure_win(session_t *ps, XConfigureEvent *ce) {
+  // On root window changes
   if (ce->window == ps->root) {
     if (ps->tgt_buffer) {
       XRenderFreePicture(ps->dpy, ps->tgt_buffer);
@@ -2618,9 +2677,15 @@ configure_win(session_t *ps, XConfigureEvent *ce) {
 
     rebuild_screen_reg(ps);
 
+#ifdef CONFIG_VSYNC_OPENGL
+    if (BKEND_GLX == ps->o.backend)
+      glx_on_root_change(ps);
+#endif
+
     return;
   }
 
+  // Other window changes
   win *w = find_win(ps, ce->window);
   XserverRegion damage = None;
 
@@ -2660,10 +2725,8 @@ configure_win(session_t *ps, XConfigureEvent *ce) {
     w->a.y = ce->y;
 
     if (w->a.width != ce->width || w->a.height != ce->height
-        || w->a.border_width != ce->border_width) {
-      free_pixmap(ps, &w->pixmap);
-      free_picture(ps, &w->picture);
-    }
+        || w->a.border_width != ce->border_width)
+      free_paint(ps, &w->paint);
 
     if (w->a.width != ce->width || w->a.height != ce->height
         || w->a.border_width != ce->border_width) {
@@ -2756,11 +2819,10 @@ destroy_win(session_t *ps, Window id) {
 
 static inline void
 root_damaged(session_t *ps) {
-  if (ps->root_tile) {
+  if (ps->root_tile_paint.pixmap) {
     XClearArea(ps->dpy, ps->root, 0, 0, 0, 0, true);
     // if (ps->root_picture != ps->root_tile) {
-      XRenderFreePicture(ps->dpy, ps->root_tile);
-      ps->root_tile = None;
+      free_root_tile(ps);
     /* }
     if (root_damage) {
       XserverRegion parts = XFixesCreateRegion(ps->dpy, 0, 0);
@@ -2829,6 +2891,21 @@ error(Display __attribute__((unused)) *dpy, XErrorEvent *ev) {
     CASESTRRET2(BadGlyph);
   }
 
+#ifdef CONFIG_VSYNC_OPENGL
+  if (ps->glx_exists) {
+    o = ev->error_code - ps->glx_error;
+    switch (o) {
+      CASESTRRET2(GLX_BAD_SCREEN);
+      CASESTRRET2(GLX_BAD_ATTRIBUTE);
+      CASESTRRET2(GLX_NO_EXTENSION);
+      CASESTRRET2(GLX_BAD_VISUAL);
+      CASESTRRET2(GLX_BAD_CONTEXT);
+      CASESTRRET2(GLX_BAD_VALUE);
+      CASESTRRET2(GLX_BAD_ENUM);
+    }
+  }
+#endif
+
   switch (ev->error_code) {
     CASESTRRET2(BadAccess);
     CASESTRRET2(BadAlloc);
@@ -2852,9 +2929,13 @@ error(Display __attribute__((unused)) *dpy, XErrorEvent *ev) {
 #undef CASESTRRET2
 
   print_timestamp(ps);
-  printf("error %d (%s) request %d minor %d serial %lu\n",
-    ev->error_code, name, ev->request_code,
-    ev->minor_code, ev->serial);
+  {
+    char buf[BUF_LEN] = "";
+    XGetErrorText(ps->dpy, ev->error_code, buf, BUF_LEN);
+    printf("error %d (%s) request %d minor %d serial %lu (\"%s\")\n",
+        ev->error_code, name, ev->request_code,
+        ev->minor_code, ev->serial, buf);
+  }
 
   return 0;
 }
@@ -3942,8 +4023,7 @@ usage(void) {
     "  Specify refresh rate of the screen. If not specified or 0, compton\n"
     "  will try detecting this with X RandR extension.\n"
     "--vsync vsync-method\n"
-    "  Set VSync method. There are up to 3 VSync methods currently available\n"
-    "  depending on your compile time settings:\n"
+    "  Set VSync method. There are up to 4 VSync methods currently available.\n"
     "    none = No VSync\n"
 #undef WARNING
 #ifndef CONFIG_VSYNC_DRM
@@ -3952,7 +4032,7 @@ usage(void) {
 #define WARNING
 #endif
     "    drm = VSync with DRM_IOCTL_WAIT_VBLANK. May only work on some\n"
-    "      drivers. Experimental." WARNING "\n"
+    "      drivers." WARNING "\n"
 #undef WARNING
 #ifndef CONFIG_VSYNC_OPENGL
 #define WARNING WARNING_DISABLED
@@ -3960,9 +4040,14 @@ usage(void) {
 #define WARNING
 #endif
     "    opengl = Try to VSync with SGI_swap_control OpenGL extension. Only\n"
-    "      work on some drivers. Experimental." WARNING"\n"
+    "      work on some drivers." WARNING"\n"
     "    opengl-oml = Try to VSync with OML_sync_control OpenGL extension.\n"
     "      Only work on some drivers. Experimental." WARNING"\n"
+    "    opengl-swc = Try to VSync with SGI_swap_control OpenGL extension.\n"
+    "      Only work on some drivers. Works only with OpenGL backend.\n"
+    "      Does not actually control paint timing, only buffer swap is\n"
+    "      affected, so it doesn't have the effect of --sw-opti unlike\n"
+    "      other methods. Experimental." WARNING "\n"
     "--alpha-step val\n"
     "  Step for pregenerating alpha pictures. 0.01 - 1.0. Defaults to\n"
     "  0.03.\n"
@@ -4013,6 +4098,8 @@ usage(void) {
     "--invert-color-include condition\n"
     "  Specify a list of conditions of windows that should be painted with\n"
     "  inverted color. Resource-hogging, and is not well tested.\n"
+    "--opengl\n"
+    "  Enable the highly experimental OpenGL backend." WARNING "\n"
 #undef WARNING
 #ifndef CONFIG_DBUS
 #define WARNING WARNING_DISABLED
@@ -4096,6 +4183,14 @@ fork_after(session_t *ps) {
   if (getppid() == 1)
     return true;
 
+#ifdef CONFIG_VSYNC_OPENGL
+  // GLX context must be released and reattached on fork
+  if (ps->glx_context && !glXMakeCurrent(ps->dpy, None, NULL)) {
+    printf_errf("(): Failed to detach GLx context.");
+    return false;
+  }
+#endif
+
   int pid = fork();
 
   if (-1 == pid) {
@@ -4107,6 +4202,14 @@ fork_after(session_t *ps) {
 
   setsid();
 
+#ifdef CONFIG_VSYNC_OPENGL
+  if (ps->glx_context
+      && !glXMakeCurrent(ps->dpy, get_tgt_window(ps), ps->glx_context)) {
+    printf_errf("(): Failed to make GLX context current.");
+    return false;
+  }
+#endif
+
   // Mainly to suppress the _FORTIFY_SOURCE warning
   bool success = freopen("/dev/null", "r", stdin);
   if (!success) {
@@ -4448,6 +4551,7 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) {
     { "dbus", no_argument, NULL, 286 },
     { "logpath", required_argument, NULL, 287 },
     { "invert-color-include", required_argument, NULL, 288 },
+    { "opengl", no_argument, NULL, 289 },
     // Must terminate with a NULL entry
     { NULL, 0, NULL, 0 },
   };
@@ -4701,6 +4805,10 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) {
         // --invert-color-include
         condlst_add(ps, &ps->o.invert_color_list, optarg);
         break;
+      case 289:
+        // --opengl
+        ps->o.backend = BKEND_GLX;
+        break;
       default:
         usage();
         break;
@@ -4889,155 +4997,6 @@ swopti_handle_timeout(session_t *ps, struct timeval *ptv) {
   }
 }
 
-#ifdef CONFIG_VSYNC_OPENGL
-/**
- * Get a GLX FBConfig.
- */
-static inline bool
-opengl_update_fbconfig(session_t *ps, bool alpha, struct glx_fbconfig **ppcfg) {
-  const int FBCONFIG_ATTRS[] = {
-    (alpha ? GLX_BIND_TO_TEXTURE_RGBA_EXT: GLX_BIND_TO_TEXTURE_RGB_EXT), True,
-    GLX_DRAWABLE_TYPE, GLX_PIXMAP_BIT,
-    GLX_BIND_TO_TEXTURE_TARGETS_EXT, GLX_TEXTURE_2D_BIT_EXT,
-    GLX_DOUBLEBUFFER, True,
-    GLX_Y_INVERTED_EXT, GLX_DONT_CARE,
-    None
-  };
-
-  if (*ppcfg) {
-    free(*ppcfg);
-    *ppcfg = NULL;
-  }
-
-  int count = 0;
-  GLXFBConfig *cfgs = glXChooseFBConfig(ps->dpy, ps->scr, FBCONFIG_ATTRS, &count);
-  if (!count || !cfgs) {
-    if (cfgs)
-      XFree(cfgs);
-    return false;
-  };
-
-  *ppcfg = malloc(sizeof(struct glx_fbconfig));
-  (*ppcfg)->cfg = cfgs[0];
-
-  {
-    int inverted = 0;
-    glXGetFBConfigAttrib(ps->dpy, (*ppcfg)->cfg, GLX_Y_INVERTED_EXT, &inverted);
-    (*ppcfg)->y_inverted = inverted;
-  }
-
-  XFree(cfgs);
-
-  return true;
-}
-
-/**
- * Initialize OpenGL.
- */
-static bool
-opengl_init(session_t *ps, bool need_render) {
-  // Check for GLX extension
-  if (!ps->glx_exists) {
-    if (glXQueryExtension(ps->dpy, &ps->glx_event, &ps->glx_error))
-      ps->glx_exists = true;
-    else {
-      printf_errf("(): No GLX extension.");
-      goto opengl_init_err;
-    }
-  }
-
-  // Ensure GLX_EXT_texture_from_pixmap exists
-  if (need_render && !opengl_hasext(ps, "GLX_EXT_texture_from_pixmap")) {
-      goto opengl_init_err;
-  }
-
-  {
-    // Get XVisualInfo
-    XVisualInfo *pvis = NULL;
-    {
-      XVisualInfo vreq = { .visualid = XVisualIDFromVisual(ps->vis) };
-      int nitems = 0;
-      pvis = XGetVisualInfo(ps->dpy, VisualIDMask, &vreq, &nitems);
-    }
-
-    if (!pvis) {
-      printf_errf("(): Failed to acquire XVisualInfo for current visual.");
-      goto opengl_init_err;
-    }
-
-    // Ensure the visual is double-buffered
-    if (need_render) {
-      int value = 0;
-      glXGetConfig(ps->dpy, pvis, GLX_DOUBLEBUFFER, &value);
-      if (!value) {
-        XFree(pvis);
-        printf_errf("(): Default GLX visual is not double-buffered.");
-        goto opengl_init_err;
-      }
-    }
-
-    // Get GLX context
-    ps->glx_context = glXCreateContext(ps->dpy, pvis, None, GL_TRUE);
-    XFree(pvis);
-  }
-
-  if (!ps->glx_context) {
-    printf_errf("(): Failed to get GLX context.");
-    goto opengl_init_err;
-  }
-
-  // Attach GLX context
-  if (!glXMakeCurrent(ps->dpy, get_tgt_window(ps), ps->glx_context)) {
-    printf_errf("(): Failed to attach GLX context.");
-    goto opengl_init_err;
-  }
-
-  // Acquire function addresses
-  if (need_render) {
-    ps->glXBindTexImageEXT = (f_BindTexImageEXT)
-      glXGetProcAddress((const GLubyte *) "glXBindTexImageEXT");
-    ps->glXReleaseTexImageEXT = (f_ReleaseTexImageEXT)
-      glXGetProcAddress((const GLubyte *) "glXReleaseTexImageEXT");
-    if (!ps->glXBindTexImageEXT || !ps->glXReleaseTexImageEXT) {
-      printf_errf("(): Failed to acquire glXBindTexImageEXT() / glXReleaseTexImageEXT().");
-      goto opengl_init_err;
-    }
-  }
-
-  // Acquire FBConfigs
-  if (need_render
-      && (!(ps->glx_fbconfig_rgb || opengl_update_fbconfig(ps, false, &ps->glx_fbconfig_rgb))
-        || !(ps->glx_fbconfig_rgba || opengl_update_fbconfig(ps, true, &ps->glx_fbconfig_rgba)))) {
-    printf_errf("(): Failed to acquire GLX fbconfig.");
-    goto opengl_init_err;
-  }
-
-  if (need_render) {
-    glViewport(0, 0, ps->root_width, ps->root_height);
-  }
-
-  return true;
-
-opengl_init_err:
-  opengl_destroy(ps);
-
-  return false;
-}
-
-static void
-opengl_destroy(session_t *ps) {
-  if (ps->glx_context) {
-    glXDestroyContext(ps->dpy, ps->glx_context);
-    ps->glx_context = None;
-  }
-
-  free(ps->glx_fbconfig_rgb);
-  ps->glx_fbconfig_rgb = NULL;
-  free(ps->glx_fbconfig_rgba);
-  ps->glx_fbconfig_rgba = NULL;
-}
-#endif
-
 /**
  * Initialize DRM VSync.
  *
@@ -5148,6 +5107,34 @@ vsync_opengl_oml_init(session_t *ps) {
 #endif
 }
 
+static bool
+vsync_opengl_swc_init(session_t *ps) {
+#ifdef CONFIG_VSYNC_OPENGL
+  if (!ensure_glx_context(ps))
+    return false;
+
+  if (BKEND_GLX != ps->o.backend) {
+    printf_errf("(): I'm afraid glXSwapIntervalSGI wouldn't help if you are "
+        "not using OpenGL backend. You could try, nonetheless.");
+  }
+
+  // Get video sync functions
+  if (!ps->glXSwapIntervalProc)
+    ps->glXSwapIntervalProc = (f_SwapIntervalSGI)
+      glXGetProcAddress ((const GLubyte *) "glXSwapIntervalSGI");
+  if (!ps->glXSwapIntervalProc) {
+    printf_errf("(): Failed to get SGI_swap_control function.");
+    return false;
+  }
+  ps->glXSwapIntervalProc(1);
+
+  return true;
+#else
+  printf_errf("(): Program not compiled with OpenGL VSync support.");
+  return false;
+#endif
+}
+
 #ifdef CONFIG_VSYNC_OPENGL
 /**
  * Wait for next VSync, OpenGL method.
@@ -5202,8 +5189,6 @@ vsync_wait(session_t *ps) {
   if (!ps->o.vsync)
     return;
 
-  assert(VSYNC_FUNCS_WAIT[ps->o.vsync]);
-
   if (VSYNC_FUNCS_WAIT[ps->o.vsync])
     VSYNC_FUNCS_WAIT[ps->o.vsync](ps);
 }
@@ -5271,29 +5256,42 @@ init_overlay(session_t *ps) {
 }
 
 /**
- * Query needed X Render filters to check for their existence.
+ * Query needed X Render / OpenGL filters to check for their existence.
  */
-static void
+static bool
 init_filters(session_t *ps) {
+  // Blur filter
   if (ps->o.blur_background || ps->o.blur_background_frame) {
-    // Query filters
-    XFilters *pf = XRenderQueryFilters(ps->dpy, get_tgt_window(ps));
-    if (pf) {
-      for (int i = 0; i < pf->nfilter; ++i) {
-        // Convolution filter
-        if (!strcmp(pf->filter[i], XRFILTER_CONVOLUTION))
-          ps->xrfilter_convolution_exists = true;
-      }
-    }
-    XFree(pf);
+    switch (ps->o.backend) {
+      case BKEND_XRENDER:
+        {
+          // Query filters
+          XFilters *pf = XRenderQueryFilters(ps->dpy, get_tgt_window(ps));
+          if (pf) {
+            for (int i = 0; i < pf->nfilter; ++i) {
+              // Convolution filter
+              if (!strcmp(pf->filter[i], XRFILTER_CONVOLUTION))
+                ps->xrfilter_convolution_exists = true;
+            }
+          }
+          XFree(pf);
 
-    // Turn features off if any required filter is not present
-    if (!ps->xrfilter_convolution_exists) {
-      fprintf(stderr, "X Render convolution filter unsupported by your X server. Background blur disabled.\n");
-      ps->o.blur_background = false;
-      ps->o.blur_background_frame = false;
+          // Turn features off if any required filter is not present
+          if (!ps->xrfilter_convolution_exists) {
+            printf_errf("(): X Render convolution filter unsupported by your X server. Background blur is not possible.");
+            return false;
+          }
+          break;
+        }
+      case BKEND_GLX:
+        {
+          printf_errf("(): OpenGL blur is not implemented yet.");
+          return false;
+        }
     }
   }
+
+  return true;
 }
 
 /**
@@ -5477,10 +5475,8 @@ redir_stop(session_t *ps) {
     // Destroy all Pictures as they expire once windows are unredirected
     // If we don't destroy them here, looks like the resources are just
     // kept inaccessible somehow
-    for (win *w = ps->list; w; w = w->next) {
-      free_pixmap(ps, &w->pixmap);
-      free_picture(ps, &w->picture);
-    }
+    for (win *w = ps->list; w; w = w->next)
+      free_paint(ps, &w->paint);
 
     XCompositeUnredirectSubwindows(ps->dpy, ps->root, CompositeRedirectManual);
     // Unmap overlay window
@@ -5597,14 +5593,17 @@ session_init(session_t *ps_old, int argc, char **argv) {
     .root_width = 0,
     // .root_damage = None,
     .overlay = None,
-    .root_tile = None,
+    .root_tile_fill = false,
+    .root_tile_paint = PAINT_INIT,
     .screen_reg = None,
     .tgt_picture = None,
     .tgt_buffer = None,
     .root_dbe = None,
     .reg_win = None,
     .o = {
+      .config_file = NULL,
       .display = NULL,
+      .backend = BKEND_XRENDER,
       .mark_wmwin_focused = false,
       .mark_ovredir_focused = false,
       .fork_after_register = false,
@@ -5863,14 +5862,35 @@ session_init(session_t *ps_old, int argc, char **argv) {
       ps->o.dbe = false;
   }
 
+  ps->root_width = DisplayWidth(ps->dpy, ps->scr);
+  ps->root_height = DisplayHeight(ps->dpy, ps->scr);
+
+  rebuild_screen_reg(ps);
+
   // Overlay must be initialized before double buffer, and before creation
   // of OpenGL context.
   if (ps->o.paint_on_overlay)
     init_overlay(ps);
 
+  // Initialize DBE
+  if (ps->o.dbe && BKEND_GLX == ps->o.backend) {
+    printf_errf("(): DBE couldn't be used on OpenGL backend.");
+    ps->o.dbe = false;
+  }
+
   if (ps->o.dbe && !init_dbe(ps))
     exit(1);
 
+  // Initialize OpenGL as early as possible
+  if (BKEND_GLX == ps->o.backend) {
+#ifdef CONFIG_VSYNC_OPENGL
+    if (!glx_init(ps, true))
+      exit(1);
+#else
+    printf_errfq(1, "(): OpenGL backend support not compiled in.");
+#endif
+  }
+
   // Initialize software optimization
   if (ps->o.sw_opti)
     ps->o.sw_opti = swopti_init(ps);
@@ -5889,11 +5909,6 @@ session_init(session_t *ps_old, int argc, char **argv) {
   ps->gaussian_map = make_gaussian_map(ps->o.shadow_radius);
   presum_gaussian(ps, ps->gaussian_map);
 
-  ps->root_width = DisplayWidth(ps->dpy, ps->scr);
-  ps->root_height = DisplayHeight(ps->dpy, ps->scr);
-
-  rebuild_screen_reg(ps);
-
   {
     XRenderPictureAttributes pa;
     pa.subwindow_mode = IncludeInferiors;
@@ -5911,7 +5926,8 @@ session_init(session_t *ps_old, int argc, char **argv) {
     }
   }
 
-  init_filters(ps);
+  if (!init_filters(ps))
+    exit(1);
 
   ps->black_picture = solid_picture(ps, true, 1, 0, 0, 0);
   ps->white_picture = solid_picture(ps, true, 1, 1, 1, 1);
@@ -6092,7 +6108,7 @@ session_destroy(session_t *ps) {
   free_picture(ps, &ps->root_picture);
 
   // Free other X resources
-  free_picture(ps, &ps->root_tile);
+  free_root_tile(ps);
   free_region(ps, &ps->screen_reg);
   free_region(ps, &ps->all_damage);
   free(ps->expose_rects);
@@ -6106,14 +6122,8 @@ session_destroy(session_t *ps) {
   free(ps->pfds_write);
   free(ps->pfds_except);
 
-  // Free reg_win and glx_context
-  if (ps->reg_win) {
-    XDestroyWindow(ps->dpy, ps->reg_win);
-    ps->reg_win = None;
-  }
-
 #ifdef CONFIG_VSYNC_OPENGL
-  opengl_destroy(ps);
+  glx_destroy(ps);
 #endif
 
   // Free double buffer
@@ -6136,6 +6146,12 @@ session_destroy(session_t *ps) {
     ps->overlay = None;
   }
 
+  // Free reg_win
+  if (ps->reg_win) {
+    XDestroyWindow(ps->dpy, ps->reg_win);
+    ps->reg_win = None;
+  }
+
   // Flush all events
   XSync(ps->dpy, True);
 
@@ -6236,7 +6252,7 @@ main(int argc, char **argv) {
   while (1) {
     ps_g = session_init(ps_old, argc, argv);
     if (!ps_g) {
-      printf_errf("Failed to create new session.");
+      printf_errf("(): Failed to create new session.");
       return 1;
     }
     session_run(ps_g);
diff --git a/compton.h b/compton.h
index f0af56482..0479b3d0a 100644
--- a/compton.h
+++ b/compton.h
@@ -9,7 +9,6 @@
 
 #include "common.h"
 
-#include <ctype.h>
 #include <math.h>
 #include <sys/select.h>
 #include <limits.h>
@@ -52,14 +51,6 @@ set_ignore_next(session_t *ps) {
 static int
 should_ignore(session_t *ps, unsigned long sequence);
 
-/**
- * Return the painting target window.
- */
-static inline Window
-get_tgt_window(session_t *ps) {
-  return ps->o.paint_on_overlay ? ps->overlay: ps->root;
-}
-
 /**
  * Reset filter on a <code>Picture</code>.
  */
@@ -119,16 +110,6 @@ array_wid_exists(const Window *arr, int count, Window wid) {
 
   return false;
 }
-/**
- * Destroy a <code>XserverRegion</code>.
- */
-inline static void
-free_region(session_t *ps, XserverRegion *p) {
-  if (*p) {
-    XFixesDestroyRegion(ps->dpy, *p);
-    *p = None;
-  }
-}
 
 /**
  * Destroy a <code>Picture</code>.
@@ -176,16 +157,58 @@ free_wincondlst(c2_lptr_t **pcondlst) {
 }
 #endif
 
+/**
+ * Check whether a paint_t contains enough data.
+ */
+static inline bool
+paint_isvalid(session_t *ps, const paint_t *ppaint) {
+  if (!ppaint || !ppaint->pixmap)
+    return false;
+
+  if (BKEND_XRENDER == ps->o.backend && !ppaint->pict)
+    return false;
+
+#ifdef CONFIG_VSYNC_OPENGL
+  if (BKEND_GLX == ps->o.backend && !glx_tex_binded(ppaint->ptex))
+    return false;
+#endif
+
+  return true;
+}
+/**
+ * Bind texture in paint_t if we are using OpenGL backend.
+ */
+static inline bool
+paint_bind_tex(session_t *ps, paint_t *ppaint, int wid, int hei, int depth) {
+#ifdef CONFIG_VSYNC_OPENGL
+  // TODO: Make sure we have the same Pixmap binded?
+  if (BKEND_GLX == ps->o.backend && !glx_tex_binded(ppaint->ptex)) {
+    return glx_bind_pixmap(ps, &ppaint->ptex, ppaint->pixmap, wid, hei, depth);
+  }
+#endif
+
+  return true;
+}
+
+/**
+ * Free paint_t.
+ */
+static inline void
+free_paint(session_t *ps, paint_t *ppaint) {
+  free_texture(ps, &ppaint->ptex);
+  free_picture(ps, &ppaint->pict);
+  free_pixmap(ps, &ppaint->pixmap);
+}
+
 /**
  * Destroy all resources in a <code>struct _win</code>.
  */
-inline static void
+static inline void
 free_win_res(session_t *ps, win *w) {
   free_region(ps, &w->extents);
-  free_pixmap(ps, &w->pixmap);
-  free_picture(ps, &w->picture);
+  free_paint(ps, &w->paint);
   free_region(ps, &w->border_size);
-  free_picture(ps, &w->shadow_pict);
+  free_paint(ps, &w->shadow_paint);
   free_damage(ps, &w->damage);
   free_region(ps, &w->reg_ignore);
   free(w->name);
@@ -194,6 +217,19 @@ free_win_res(session_t *ps, win *w) {
   free(w->role);
 }
 
+/**
+ * Free root tile related things.
+ */
+static inline void
+free_root_tile(session_t *ps) {
+  free_picture(ps, &ps->root_tile_paint.pict);
+  free_texture(ps, &ps->root_tile_paint.ptex);
+  if (ps->root_tile_fill)
+    free_pixmap(ps, &ps->root_tile_paint.pixmap);
+  ps->root_tile_paint.pixmap = None;
+  ps->root_tile_fill = false;
+}
+
 /**
  * Get current system clock in milliseconds.
  */
@@ -274,8 +310,8 @@ presum_gaussian(session_t *ps, conv *map);
 static XImage *
 make_shadow(session_t *ps, double opacity, int width, int height);
 
-static Picture
-shadow_picture(session_t *ps, double opacity, int width, int height);
+static bool
+win_build_shadow(session_t *ps, win *w, double opacity);
 
 static Picture
 solid_picture(session_t *ps, bool argb, double a,
@@ -463,11 +499,11 @@ group_is_focused(session_t *ps, Window leader) {
 static win *
 recheck_focus(session_t *ps);
 
-static Picture
-root_tile_f(session_t *ps);
+static bool
+get_root_tile(session_t *ps);
 
 static void
-paint_root(session_t *ps, Picture tgt_buffer);
+paint_root(session_t *ps, XserverRegion reg_paint);
 
 static XserverRegion
 win_get_region(session_t *ps, win *w, bool use_offset);
@@ -490,6 +526,22 @@ get_frame_extents(session_t *ps, win *w, Window client);
 static win *
 paint_preprocess(session_t *ps, win *list);
 
+static void
+render(session_t *ps, int x, int y, int dx, int dy, int wid, int hei,
+    double opacity, bool argb, bool neg,
+    Picture pict, glx_texture_t *ptex, XserverRegion reg_paint);
+
+static inline void
+win_render(session_t *ps, win *w, int x, int y, int wid, int hei, double opacity, XserverRegion reg_paint, Picture pict) {
+  const int dx = (w ? w->a.x: 0) + x;
+  const int dy = (w ? w->a.y: 0) + y;
+  const bool argb = (w && w->mode == WMODE_ARGB);
+  const bool neg = (w && w->invert_color);
+
+  render(ps, x, y, dx, dy, wid, hei, opacity, argb, neg,
+      pict, (w ? w->paint.ptex: ps->root_tile_paint.ptex), reg_paint);
+}
+
 static void
 paint_all(session_t *ps, XserverRegion region, win *t);
 
@@ -887,31 +939,7 @@ swopti_init(session_t *ps);
 static void
 swopti_handle_timeout(session_t *ps, struct timeval *ptv);
 
-static bool
-opengl_init(session_t *ps, bool need_render);
-
-static void
-opengl_destroy(session_t *ps);
-
 #ifdef CONFIG_VSYNC_OPENGL
-/**
- * Check if a GLX extension exists.
- */
-static inline bool
-opengl_hasext(session_t *ps, const char *ext) {
-  const char *glx_exts = glXQueryExtensionsString(ps->dpy, ps->scr);
-  const char *pos = strstr(glx_exts, ext);
-  // Make sure the extension string is matched as a whole word
-  if (!pos
-      || ((pos - glx_exts) && !isspace(*(pos - 1)))
-      || (strlen(pos) > strlen(ext) && !isspace(pos[strlen(ext)]))) {
-    printf_errf("(): Missing OpenGL extension %s.", ext);
-    return false;
-  }
-
-  return true;
-}
-
 /**
  * Ensure we have a GLX context.
  */
@@ -919,7 +947,7 @@ static inline bool
 ensure_glx_context(session_t *ps) {
   // Create GLX context
   if (!ps->glx_context)
-    opengl_init(ps, false);
+    glx_init(ps, false);
 
   return ps->glx_context;
 }
@@ -939,6 +967,9 @@ vsync_opengl_init(session_t *ps);
 static bool
 vsync_opengl_oml_init(session_t *ps);
 
+static bool
+vsync_opengl_swc_init(session_t *ps);
+
 #ifdef CONFIG_VSYNC_OPENGL
 static int
 vsync_opengl_wait(session_t *ps);
diff --git a/opengl.c b/opengl.c
new file mode 100644
index 000000000..16e99de02
--- /dev/null
+++ b/opengl.c
@@ -0,0 +1,521 @@
+/*
+ * Compton - a compositor for X11
+ *
+ * Based on `xcompmgr` - Copyright (c) 2003, Keith Packard
+ *
+ * Copyright (c) 2011-2013, Christopher Jeffrey
+ * See LICENSE for more information.
+ *
+ */
+
+#include "opengl.h"
+
+/**
+ * Initialize OpenGL.
+ */
+bool
+glx_init(session_t *ps, bool need_render) {
+  bool success = false;
+  XVisualInfo *pvis = NULL;
+
+  // Check for GLX extension
+  if (!ps->glx_exists) {
+    if (glXQueryExtension(ps->dpy, &ps->glx_event, &ps->glx_error))
+      ps->glx_exists = true;
+    else {
+      printf_errf("(): No GLX extension.");
+      goto glx_init_end;
+    }
+  }
+
+  // Get XVisualInfo
+  pvis = get_visualinfo_from_visual(ps, ps->vis);
+  if (!pvis) {
+    printf_errf("(): Failed to acquire XVisualInfo for current visual.");
+    goto glx_init_end;
+  }
+
+  // Ensure the visual is double-buffered
+  if (need_render) {
+    int value = 0;
+    if (Success != glXGetConfig(ps->dpy, pvis, GLX_USE_GL, &value) || !value) {
+      printf_errf("(): Root visual is not a GL visual.");
+      goto glx_init_end;
+    }
+
+    if (Success != glXGetConfig(ps->dpy, pvis, GLX_DOUBLEBUFFER, &value)
+        || !value) {
+      printf_errf("(): Root visual is not a double buffered GL visual.");
+      goto glx_init_end;
+    }
+  }
+
+  // Ensure GLX_EXT_texture_from_pixmap exists
+  if (need_render && !glx_hasext(ps, "GLX_EXT_texture_from_pixmap"))
+    goto glx_init_end;
+
+  // Get GLX context
+  ps->glx_context = glXCreateContext(ps->dpy, pvis, None, GL_TRUE);
+
+  if (!ps->glx_context) {
+    printf_errf("(): Failed to get GLX context.");
+    goto glx_init_end;
+  }
+
+  // Attach GLX context
+  if (!glXMakeCurrent(ps->dpy, get_tgt_window(ps), ps->glx_context)) {
+    printf_errf("(): Failed to attach GLX context.");
+    goto glx_init_end;
+  }
+
+  // Acquire function addresses
+  if (need_render) {
+    ps->glXBindTexImageProc = (f_BindTexImageEXT)
+      glXGetProcAddress((const GLubyte *) "glXBindTexImageEXT");
+    ps->glXReleaseTexImageProc = (f_ReleaseTexImageEXT)
+      glXGetProcAddress((const GLubyte *) "glXReleaseTexImageEXT");
+    if (!ps->glXBindTexImageProc || !ps->glXReleaseTexImageProc) {
+      printf_errf("(): Failed to acquire glXBindTexImageEXT() / glXReleaseTexImageEXT().");
+      goto glx_init_end;
+    }
+  }
+
+  // Acquire FBConfigs
+  if (need_render && !glx_update_fbconfig(ps))
+    goto glx_init_end;
+
+  if (need_render) {
+    // Adjust viewport
+    glViewport(0, 0, ps->root_width, ps->root_height);
+
+    // Initialize settings, copied from dcompmgr
+    glMatrixMode(GL_PROJECTION);
+    glLoadIdentity();
+    glOrtho(0, ps->root_width, ps->root_height, 0, -100.0, 100.0);
+    glMatrixMode(GL_MODELVIEW);
+    glLoadIdentity();
+    // glEnable(GL_DEPTH_TEST);
+    glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
+    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+    glDisable(GL_BLEND);
+
+    // Clear screen
+    glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
+    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+    glXSwapBuffers(ps->dpy, get_tgt_window(ps));
+  }
+
+  success = true;
+
+glx_init_end:
+  if (pvis)
+    XFree(pvis);
+
+  if (!success)
+    glx_destroy(ps);
+
+  return success;
+}
+
+/**
+ * Destroy GLX related resources.
+ */
+void
+glx_destroy(session_t *ps) {
+  // Free FBConfigs
+  for (int i = 0; i <= OPENGL_MAX_DEPTH; ++i) {
+    free(ps->glx_fbconfigs[i]);
+    ps->glx_fbconfigs[i] = NULL;
+  }
+
+  // Destroy GLX context
+  if (ps->glx_context) {
+    glXDestroyContext(ps->dpy, ps->glx_context);
+    ps->glx_context = NULL;
+  }
+}
+
+/**
+ * Callback to run on root window size change.
+ */
+void
+glx_on_root_change(session_t *ps) {
+  glViewport(0, 0, ps->root_width, ps->root_height);
+}
+
+/**
+ * @brief Update the FBConfig of given depth.
+ */
+static inline void
+glx_update_fbconfig_bydepth(session_t *ps, int depth, glx_fbconfig_t *pfbcfg) {
+  // Make sure the depth is sane
+  if (depth < 0 || depth > OPENGL_MAX_DEPTH)
+    return;
+
+  // Compare new FBConfig with current one
+  if (glx_cmp_fbconfig(ps, ps->glx_fbconfigs[depth], pfbcfg) < 0) {
+#ifdef DEBUG_GLX
+    printf_dbgf("(%d): %#x overrides %#x, target %#x.\n", depth, (unsigned) pfbcfg->cfg, (ps->glx_fbconfigs[depth] ? (unsigned) ps->glx_fbconfigs[depth]->cfg: 0), pfbcfg->texture_tgts);
+#endif
+    if (!ps->glx_fbconfigs[depth]) {
+      ps->glx_fbconfigs[depth] = malloc(sizeof(glx_fbconfig_t));
+      allocchk(ps->glx_fbconfigs[depth]);
+    }
+    (*ps->glx_fbconfigs[depth]) = *pfbcfg;
+  }
+}
+
+/**
+ * Get GLX FBConfigs for all depths.
+ */
+static bool
+glx_update_fbconfig(session_t *ps) {
+  // Acquire all FBConfigs and loop through them
+  int nele = 0;
+  GLXFBConfig* pfbcfgs = glXGetFBConfigs(ps->dpy, ps->scr, &nele);
+
+  for (GLXFBConfig *pcur = pfbcfgs; pcur < pfbcfgs + nele; pcur++) {
+    glx_fbconfig_t fbinfo = {
+      .cfg = *pcur,
+      .texture_fmt = 0,
+      .texture_tgts = 0,
+      .y_inverted = false,
+    };
+    int depth = 0, depth_alpha = 0, val = 0;
+
+    if (Success != glXGetFBConfigAttrib(ps->dpy, *pcur, GLX_BUFFER_SIZE, &depth)
+        || Success != glXGetFBConfigAttrib(ps->dpy, *pcur, GLX_ALPHA_SIZE, &depth_alpha)) {
+      printf_errf("(): Failed to retrieve buffer size and alpha size of FBConfig %d.", (int) (pcur - pfbcfgs));
+      continue;
+    }
+    if (Success != glXGetFBConfigAttrib(ps->dpy, *pcur, GLX_BIND_TO_TEXTURE_TARGETS_EXT, &fbinfo.texture_tgts)) {
+      printf_errf("(): Failed to retrieve BIND_TO_TEXTURE_TARGETS_EXT of FBConfig %d.", (int) (pcur - pfbcfgs));
+      continue;
+    }
+
+    bool rgb = false;
+    bool rgba = false;
+
+    if (depth >= 32 && depth_alpha && Success == glXGetFBConfigAttrib(ps->dpy, *pcur, GLX_BIND_TO_TEXTURE_RGBA_EXT, &val) && val)
+      rgba = true;
+
+    if (Success == glXGetFBConfigAttrib(ps->dpy, *pcur, GLX_BIND_TO_TEXTURE_RGB_EXT, &val) && val)
+      rgb = true;
+
+    if (Success == glXGetFBConfigAttrib(ps->dpy, *pcur, GLX_Y_INVERTED_EXT, &val))
+      fbinfo.y_inverted = val;
+
+    if ((depth - depth_alpha) < 32 && rgb) {
+      fbinfo.texture_fmt = GLX_TEXTURE_FORMAT_RGB_EXT;
+      glx_update_fbconfig_bydepth(ps, depth - depth_alpha, &fbinfo);
+    }
+
+    if (rgba) {
+      fbinfo.texture_fmt = GLX_TEXTURE_FORMAT_RGBA_EXT;
+      glx_update_fbconfig_bydepth(ps, depth, &fbinfo);
+    }
+  }
+
+  if (pfbcfgs)
+    XFree(pfbcfgs);
+
+  // Sanity checks
+  if (!ps->glx_fbconfigs[ps->depth]) {
+    printf_errf("(): No FBConfig found for default depth %d.", ps->depth);
+    return false;
+  }
+
+  if (!ps->glx_fbconfigs[32]) {
+    printf_errf("(): No FBConfig found for depth 32. Expect crazy things.");
+  }
+
+  return true;
+}
+
+static inline int
+glx_cmp_fbconfig_cmpattr(session_t *ps,
+    const glx_fbconfig_t *pfbc_a, const glx_fbconfig_t *pfbc_b,
+    int attr) {
+  int attr_a = 0, attr_b = 0;
+
+  // TODO: Error checking
+  glXGetFBConfigAttrib(ps->dpy, pfbc_a->cfg, attr, &attr_a);
+  glXGetFBConfigAttrib(ps->dpy, pfbc_b->cfg, attr, &attr_b);
+
+  return attr_a - attr_b;
+}
+
+/**
+ * Compare two GLX FBConfig's to find the preferred one.
+ */
+static int
+glx_cmp_fbconfig(session_t *ps,
+    const glx_fbconfig_t *pfbc_a, const glx_fbconfig_t *pfbc_b) {
+  int result = 0;
+
+  if (!pfbc_a)
+    return -1;
+  if (!pfbc_b)
+    return 1;
+
+#define P_CMPATTR_LT(attr) { if ((result = glx_cmp_fbconfig_cmpattr(ps, pfbc_a, pfbc_b, (attr)))) return -result; }
+#define P_CMPATTR_GT(attr) { if ((result = glx_cmp_fbconfig_cmpattr(ps, pfbc_a, pfbc_b, (attr)))) return result; }
+
+  P_CMPATTR_LT(GLX_BIND_TO_TEXTURE_RGBA_EXT);
+  P_CMPATTR_LT(GLX_DOUBLEBUFFER);
+  P_CMPATTR_LT(GLX_STENCIL_SIZE);
+  P_CMPATTR_LT(GLX_DEPTH_SIZE);
+  P_CMPATTR_GT(GLX_BIND_TO_MIPMAP_TEXTURE_EXT);
+
+  return 0;
+}
+
+/**
+ * Bind a X pixmap to an OpenGL texture.
+ */
+bool
+glx_bind_pixmap(session_t *ps, glx_texture_t **pptex, Pixmap pixmap,
+    int width, int height, int depth) {
+  if (depth > OPENGL_MAX_DEPTH) {
+    printf_errf("(%d): Requested depth higher than %d.", depth,
+        OPENGL_MAX_DEPTH);
+    return false;
+  }
+  const glx_fbconfig_t *pcfg = ps->glx_fbconfigs[depth];
+  if (!pcfg) {
+    printf_errf("(%d): Couldn't find FBConfig with requested depth.", depth);
+    return false;
+  }
+
+  const GLenum target = GL_TEXTURE_2D;
+  glx_texture_t *ptex = *pptex;
+  bool need_release = true;
+
+  // Allocate structure
+  if (!ptex) {
+    static const glx_texture_t GLX_TEX_DEF = {
+      .texture = 0,
+      .glpixmap = 0,
+      .pixmap = 0,
+      .width = 0,
+      .height = 0,
+      .depth = 0,
+      .y_inverted = false,
+    };
+
+    ptex = malloc(sizeof(glx_texture_t));
+    allocchk(ptex);
+    memcpy(ptex, &GLX_TEX_DEF, sizeof(glx_texture_t));
+    *pptex = ptex;
+  }
+
+  glEnable(target);
+
+  // Release pixmap if parameters are inconsistent
+  if (ptex->texture && !(width == ptex->width && height == ptex->height
+        && ptex->pixmap == pixmap && depth == ptex->depth)) {
+    glx_release_pixmap(ps, ptex);
+  }
+
+  // Create texture
+  if (!ptex->texture) {
+    need_release = false;
+
+    GLuint texture = 0;
+    glGenTextures(1, &texture);
+    glBindTexture(target, texture);
+
+    glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+    glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+    glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+    glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+    glBindTexture(target, 0);
+
+    ptex->texture = texture;
+    ptex->y_inverted = pcfg->y_inverted;
+  }
+  if (!ptex->texture) {
+    printf_errf("(): Failed to allocate texture.");
+    return false;
+  }
+
+  glBindTexture(target, ptex->texture);
+
+  // Create GLX pixmap
+  if (!ptex->glpixmap) {
+    need_release = false;
+
+#ifdef DEBUG_GLX
+    printf_dbgf("(): depth %d rgba %d\n", depth, (GLX_TEXTURE_FORMAT_RGBA_EXT == pcfg->texture_fmt));
+#endif
+
+    int attrs[] = {
+        GLX_TEXTURE_FORMAT_EXT,
+        pcfg->texture_fmt,
+        // GLX_TEXTURE_TARGET_EXT,
+        // ,
+        0,
+    };
+
+    ptex->glpixmap = glXCreatePixmap(ps->dpy, pcfg->cfg, pixmap, attrs);
+  }
+  if (!ptex->glpixmap) {
+    printf_errf("(): Failed to allocate GLX pixmap.");
+    return false;
+  }
+
+  // The specification requires rebinding whenever the content changes...
+  // We can't follow this, too slow.
+  if (need_release)
+    ps->glXReleaseTexImageProc(ps->dpy, ptex->glpixmap, GLX_FRONT_LEFT_EXT);
+
+  ps->glXBindTexImageProc(ps->dpy, ptex->glpixmap, GLX_FRONT_LEFT_EXT, NULL);
+
+  // Cleanup
+  glBindTexture(target, 0);
+  glDisable(target);
+
+  ptex->width = width;
+  ptex->height = height;
+  ptex->depth = depth;
+  ptex->pixmap = pixmap;
+
+  return true;
+}
+
+/**
+ * @brief Release binding of a texture.
+ */
+void
+glx_release_pixmap(session_t *ps, glx_texture_t *ptex) {
+  // Release binding
+  if (ptex->glpixmap && ptex->texture) {
+    glBindTexture(GL_TEXTURE_2D, ptex->texture);
+    ps->glXReleaseTexImageProc(ps->dpy, ptex->glpixmap, GLX_FRONT_LEFT_EXT);
+    glBindTexture(GL_TEXTURE_2D, 0);
+  }
+
+  // Free GLX Pixmap
+  if (ptex->glpixmap) {
+    glXDestroyPixmap(ps->dpy, ptex->glpixmap);
+    ptex->glpixmap = 0;
+  }
+}
+
+/**
+ * @brief Render a region with texture data.
+ */
+bool
+glx_render(session_t *ps, const glx_texture_t *ptex,
+    int x, int y, int dx, int dy, int width, int height, int z,
+    double opacity, bool neg, XserverRegion reg_tgt) {
+  if (!ptex || !ptex->texture) {
+    printf_errf("(): Missing texture.");
+    return false;
+  }
+
+  // Enable blending if needed
+  if (opacity < 1.0 || GLX_TEXTURE_FORMAT_RGBA_EXT ==
+      ps->glx_fbconfigs[ptex->depth]->texture_fmt) {
+    glEnable(GL_BLEND);
+    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+    // Needed for handling opacity of ARGB texture
+    glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+    glColor4f(1.0f, 1.0f, 1.0f, opacity);
+  }
+
+  // Color negation
+  if (neg) {
+    // Simple color negation
+    if (!glIsEnabled(GL_BLEND)) {
+      glEnable(GL_COLOR_LOGIC_OP);
+      glLogicOp(GL_COPY_INVERTED);
+    }
+    // Blending color negation
+    else {
+      glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
+      glTexEnvf(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_REPLACE);
+      glTexEnvf(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_ONE_MINUS_SRC_COLOR);
+    }
+  }
+
+  glEnable(GL_TEXTURE_2D);
+  glBindTexture(GL_TEXTURE_2D, ptex->texture);
+
+  glBegin(GL_QUADS);
+
+  {
+    XserverRegion reg_new = None;
+
+    XRectangle rec_all = {
+      .x = dx,
+      .y = dy,
+      .width = width,
+      .height = height
+    };
+
+    XRectangle *rects = &rec_all;
+    int nrects = 1;
+
+#ifdef DEBUG_GLX
+    printf_dbgf("(): Draw: %d, %d, %d, %d -> %d, %d (%d, %d) z %d \n", x, y, width, height, dx, dy, ptex->width, ptex->height, z);
+#endif
+
+    if (reg_tgt) {
+      reg_new = XFixesCreateRegion(ps->dpy, &rec_all, 1);
+      XFixesIntersectRegion(ps->dpy, reg_new, reg_new, reg_tgt);
+
+      nrects = 0;
+      rects = XFixesFetchRegion(ps->dpy, reg_new, &nrects);
+    }
+
+    for (int i = 0; i < nrects; ++i) {
+      GLfloat rx = (double) (rects[i].x - dx + x) / ptex->width;
+      GLfloat ry = (double) (rects[i].y - dy + y) / ptex->height;
+      GLfloat rxe = rx + (double) rects[i].width / ptex->width;
+      GLfloat rye = ry + (double) rects[i].height / ptex->height;
+      GLint rdx = rects[i].x;
+      GLint rdy = rects[i].y;
+      GLint rdxe = rdx + rects[i].width;
+      GLint rdye = rdy + rects[i].height;
+
+      // Invert Y if needed, this may not work as expected, though. I don't
+      // have such a FBConfig to test with.
+      if (!ptex->y_inverted) {
+        ry = 1.0 - ry;
+        rye = 1.0 - rye;
+      }
+
+#ifdef DEBUG_GLX
+      printf_dbgf("(): Rect %d: %f, %f, %f, %f -> %d, %d, %d, %d\n", i, rx, ry, rxe, rye, rdx, rdy, rdxe, rdye);
+#endif
+
+      glTexCoord2f(rx, ry);
+      glVertex3i(rdx, rdy, z);
+
+      glTexCoord2f(rxe, ry);
+      glVertex3i(rdxe, rdy, z);
+
+      glTexCoord2f(rxe, rye);
+      glVertex3i(rdxe, rdye, z);
+
+      glTexCoord2f(rx, rye);
+      glVertex3i(rdx, rdye, z);
+    }
+
+    if (rects && rects != &rec_all)
+      XFree(rects);
+    free_region(ps, &reg_new);
+  }
+  glEnd();
+
+  // Cleanup
+  glBindTexture(GL_TEXTURE_2D, 0);
+  glColor4f(0.0f, 0.0f, 0.0f, 0.0f);
+  glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
+  glDisable(GL_BLEND);
+  glDisable(GL_COLOR_LOGIC_OP);
+
+  return true;
+}
diff --git a/opengl.h b/opengl.h
new file mode 100644
index 000000000..b48b441d0
--- /dev/null
+++ b/opengl.h
@@ -0,0 +1,46 @@
+/*
+ * Compton - a compositor for X11
+ *
+ * Based on `xcompmgr` - Copyright (c) 2003, Keith Packard
+ *
+ * Copyright (c) 2011-2013, Christopher Jeffrey
+ * See LICENSE for more information.
+ *
+ */
+
+#include "common.h"
+
+#include <ctype.h>
+
+/**
+ * Check if a GLX extension exists.
+ */
+static inline bool
+glx_hasext(session_t *ps, const char *ext) {
+  const char *glx_exts = glXQueryExtensionsString(ps->dpy, ps->scr);
+  const char *pos = strstr(glx_exts, ext);
+  // Make sure the extension string is matched as a whole word
+  if (!pos
+      || ((pos - glx_exts) && !isspace(*(pos - 1)))
+      || (strlen(pos) > strlen(ext) && !isspace(pos[strlen(ext)]))) {
+    printf_errf("(): Missing OpenGL extension %s.", ext);
+    return false;
+  }
+
+  return true;
+}
+
+static inline XVisualInfo *
+get_visualinfo_from_visual(session_t *ps, Visual *visual) {
+  XVisualInfo vreq = { .visualid = XVisualIDFromVisual(visual) };
+  int nitems = 0;
+
+  return XGetVisualInfo(ps->dpy, VisualIDMask, &vreq, &nitems);
+}
+
+static bool
+glx_update_fbconfig(session_t *ps);
+
+static int
+glx_cmp_fbconfig(session_t *ps,
+    const glx_fbconfig_t *pfbc_a, const glx_fbconfig_t *pfbc_b);
-- 
cgit v1.2.3

