f7d48e
From 768818c8d071f066a2ab68f83381829838b5bf29 Mon Sep 17 00:00:00 2001
f7d48e
From: "Owen W. Taylor" <otaylor@fishsoup.net>
f7d48e
Date: Thu, 8 May 2014 18:44:15 -0400
f7d48e
Subject: [PATCH 1/2] Add support for quad-buffer stereo
f7d48e
f7d48e
Track the stereo status of windows using the new EXT_stereo_tree
f7d48e
GLX extension.
f7d48e
f7d48e
When stereo is enabled or disabled, a restart is triggered via
f7d48e
meta_restart() after a timeout, setting a _META_ENABLE_STEREO
f7d48e
property on the root window to indicate whether we should
f7d48e
turn on a stereo stage for clutter. The property avoids a loop,
f7d48e
since we need to enable stereo *before* initializing Clutter and GL,
f7d48e
but we need GL to figure out whether we have stereo windows.
f7d48e
f7d48e
Stereo windows are drawn to the stage using new functionality
f7d48e
in Cogl to setup a stereo context, select which buffer to draw
f7d48e
to, and draw either the left or right buffer of a stereo
f7d48e
texture_from_pixmap.
f7d48e
---
f7d48e
 clutter/clutter/clutter-paint-nodes.c        | 103 +++++++++++
f7d48e
 clutter/clutter/clutter-paint-nodes.h        |  13 ++
f7d48e
 src/compositor/compositor.c                  |   8 +
f7d48e
 src/compositor/meta-compositor-x11.c         | 127 +++++++++++++
f7d48e
 src/compositor/meta-compositor-x11.h         |   6 +
f7d48e
 src/compositor/meta-shaped-texture-private.h |   5 +-
f7d48e
 src/compositor/meta-shaped-texture.c         | 176 +++++++++++++++----
f7d48e
 src/compositor/meta-surface-actor-wayland.c  |   2 +-
f7d48e
 src/compositor/meta-surface-actor-x11.c      |  55 +++++-
f7d48e
 src/compositor/meta-surface-actor-x11.h      |   5 +
f7d48e
 src/compositor/meta-window-actor-private.h   |   5 +
f7d48e
 src/compositor/meta-window-actor.c           |  22 +++
f7d48e
 src/core/main.c                              |   4 +
f7d48e
 src/core/stereo.c                            | 154 ++++++++++++++++
f7d48e
 src/core/stereo.h                            |  28 +++
f7d48e
 src/meson.build                              |   2 +
f7d48e
 src/wayland/meta-wayland-actor-surface.c     |   4 +-
f7d48e
 17 files changed, 667 insertions(+), 52 deletions(-)
f7d48e
 create mode 100644 src/core/stereo.c
f7d48e
 create mode 100644 src/core/stereo.h
f7d48e
f7d48e
diff --git a/clutter/clutter/clutter-paint-nodes.c b/clutter/clutter/clutter-paint-nodes.c
f7d48e
index f1f7fce318..29a673e9c7 100644
f7d48e
--- a/clutter/clutter/clutter-paint-nodes.c
f7d48e
+++ b/clutter/clutter/clutter-paint-nodes.c
f7d48e
@@ -1970,3 +1970,106 @@ clutter_blur_node_new (unsigned int width,
f7d48e
 out:
f7d48e
   return (ClutterPaintNode *) blur_node;
f7d48e
 }
f7d48e
+
f7d48e
+/*
f7d48e
+ * ClutterStereoNode
f7d48e
+ */
f7d48e
+
f7d48e
+struct _ClutterStereoNode
f7d48e
+{
f7d48e
+  ClutterPaintNode parent_instance;
f7d48e
+
f7d48e
+  CoglStereoMode stereo_mode;
f7d48e
+};
f7d48e
+
f7d48e
+struct _ClutterStereoNodeClass
f7d48e
+{
f7d48e
+  ClutterPaintNodeClass parent_class;
f7d48e
+};
f7d48e
+
f7d48e
+G_DEFINE_TYPE (ClutterStereoNode, clutter_stereo_node, CLUTTER_TYPE_PAINT_NODE)
f7d48e
+
f7d48e
+static gboolean
f7d48e
+clutter_stereo_node_pre_draw (ClutterPaintNode    *node,
f7d48e
+                              ClutterPaintContext *paint_context)
f7d48e
+{
f7d48e
+  ClutterStereoNode *stereo_node = CLUTTER_STEREO_NODE (node);
f7d48e
+  CoglFramebuffer *fb =
f7d48e
+   clutter_paint_context_get_framebuffer (paint_context);
f7d48e
+
f7d48e
+  g_warn_if_fail (cogl_framebuffer_get_is_stereo (fb));
f7d48e
+
f7d48e
+  cogl_framebuffer_set_stereo_mode (fb, stereo_node->stereo_mode);
f7d48e
+
f7d48e
+  return TRUE;
f7d48e
+}
f7d48e
+
f7d48e
+static void
f7d48e
+clutter_stereo_node_post_draw (ClutterPaintNode    *node,
f7d48e
+                               ClutterPaintContext *paint_context)
f7d48e
+{
f7d48e
+  CoglFramebuffer *fb =
f7d48e
+   clutter_paint_context_get_framebuffer (paint_context);
f7d48e
+
f7d48e
+  cogl_framebuffer_set_stereo_mode (fb, COGL_STEREO_BOTH);
f7d48e
+}
f7d48e
+
f7d48e
+static const char *
f7d48e
+stereo_mode_to_string (CoglStereoMode stereo_mode)
f7d48e
+{
f7d48e
+  switch (stereo_mode)
f7d48e
+    {
f7d48e
+    case COGL_STEREO_BOTH:
f7d48e
+      return "both";
f7d48e
+    case COGL_STEREO_LEFT:
f7d48e
+      return "left";
f7d48e
+    case COGL_STEREO_RIGHT:
f7d48e
+      return "right";
f7d48e
+    }
f7d48e
+
f7d48e
+  g_assert_not_reached ();
f7d48e
+}
f7d48e
+
f7d48e
+static JsonNode *
f7d48e
+clutter_stereo_node_serialize (ClutterPaintNode *node)
f7d48e
+{
f7d48e
+  ClutterStereoNode *stereo_node = CLUTTER_STEREO_NODE (node);
f7d48e
+  g_autoptr (JsonBuilder) builder = NULL;
f7d48e
+  const char *stereo_mode_str;
f7d48e
+
f7d48e
+  builder = json_builder_new ();
f7d48e
+  json_builder_begin_object (builder);
f7d48e
+  json_builder_set_member_name (builder, "stereo-mode");
f7d48e
+  stereo_mode_str = stereo_mode_to_string (stereo_node->stereo_mode);
f7d48e
+  json_builder_add_string_value (builder, stereo_mode_str);
f7d48e
+  json_builder_end_object (builder);
f7d48e
+
f7d48e
+  return json_builder_get_root (builder);
f7d48e
+}
f7d48e
+
f7d48e
+static void
f7d48e
+clutter_stereo_node_class_init (ClutterStereoNodeClass *klass)
f7d48e
+{
f7d48e
+  ClutterPaintNodeClass *node_class;
f7d48e
+
f7d48e
+  node_class = CLUTTER_PAINT_NODE_CLASS (klass);
f7d48e
+  node_class->pre_draw = clutter_stereo_node_pre_draw;
f7d48e
+  node_class->post_draw = clutter_stereo_node_post_draw;
f7d48e
+  node_class->serialize = clutter_stereo_node_serialize;
f7d48e
+}
f7d48e
+
f7d48e
+static void
f7d48e
+clutter_stereo_node_init (ClutterStereoNode *stereo_node)
f7d48e
+{
f7d48e
+}
f7d48e
+
f7d48e
+ClutterPaintNode *
f7d48e
+clutter_stereo_node_new (CoglStereoMode stereo_mode)
f7d48e
+{
f7d48e
+  ClutterStereoNode *stereo_node;
f7d48e
+
f7d48e
+  stereo_node = _clutter_paint_node_create (CLUTTER_TYPE_STEREO_NODE);
f7d48e
+  stereo_node->stereo_mode = stereo_mode;
f7d48e
+
f7d48e
+  return CLUTTER_PAINT_NODE (stereo_node);
f7d48e
+}
f7d48e
diff --git a/clutter/clutter/clutter-paint-nodes.h b/clutter/clutter/clutter-paint-nodes.h
f7d48e
index 7f0d12857a..77d1ab05b6 100644
f7d48e
--- a/clutter/clutter/clutter-paint-nodes.h
f7d48e
+++ b/clutter/clutter/clutter-paint-nodes.h
f7d48e
@@ -284,6 +284,19 @@ ClutterPaintNode * clutter_blur_node_new (unsigned int width,
f7d48e
                                           unsigned int height,
f7d48e
                                           float        sigma);
f7d48e
 
f7d48e
+#define CLUTTER_TYPE_STEREO_NODE                (clutter_stereo_node_get_type ())
f7d48e
+#define CLUTTER_STEREO_NODE(obj)                (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_STEREO_NODE, ClutterStereoNode))
f7d48e
+#define CLUTTER_IS_STEREO_NODE(obj)             (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_STEREO_NODE))
f7d48e
+
f7d48e
+typedef struct _ClutterStereoNode               ClutterStereoNode;
f7d48e
+typedef struct _ClutterStereoNodeClass          ClutterStereoNodeClass;
f7d48e
+
f7d48e
+CLUTTER_EXPORT
f7d48e
+GType clutter_stereo_node_get_type (void) G_GNUC_CONST;
f7d48e
+
f7d48e
+CLUTTER_EXPORT
f7d48e
+ClutterPaintNode * clutter_stereo_node_new (CoglStereoMode stereo_mode);
f7d48e
+
f7d48e
 G_END_DECLS
f7d48e
 
f7d48e
 #endif /* __CLUTTER_PAINT_NODES_H__ */
f7d48e
diff --git a/src/compositor/compositor.c b/src/compositor/compositor.c
f7d48e
index 1770550d4c..a4bd1252ae 100644
f7d48e
--- a/src/compositor/compositor.c
f7d48e
+++ b/src/compositor/compositor.c
f7d48e
@@ -66,6 +66,7 @@
f7d48e
 #include "compositor/meta-window-actor-private.h"
f7d48e
 #include "compositor/meta-window-group-private.h"
f7d48e
 #include "core/frame.h"
f7d48e
+#include "core/stereo.h"
f7d48e
 #include "core/util-private.h"
f7d48e
 #include "core/window-private.h"
f7d48e
 #include "meta/compositor-mutter.h"
f7d48e
@@ -910,6 +911,7 @@ meta_compositor_sync_stack (MetaCompositor  *compositor,
f7d48e
     meta_compositor_get_instance_private (compositor);
f7d48e
   MetaWindowActor *top_window_actor;
f7d48e
   GList *old_stack;
f7d48e
+  int stereo_window_count = 0;
f7d48e
 
f7d48e
   /* This is painful because hidden windows that we are in the process
f7d48e
    * of animating out of existence. They'll be at the bottom of the
f7d48e
@@ -986,12 +988,18 @@ meta_compositor_sync_stack (MetaCompositor  *compositor,
f7d48e
        */
f7d48e
       priv->windows = g_list_prepend (priv->windows, actor);
f7d48e
 
f7d48e
+      if (meta_window_actor_is_stereo (actor))
f7d48e
+        stereo_window_count++;
f7d48e
+
f7d48e
       stack = g_list_remove (stack, window);
f7d48e
       old_stack = g_list_remove (old_stack, actor);
f7d48e
     }
f7d48e
 
f7d48e
   sync_actor_stacking (compositor);
f7d48e
 
f7d48e
+  if (!meta_is_wayland_compositor ())
f7d48e
+    meta_stereo_set_have_stereo_windows (stereo_window_count > 0);
f7d48e
+
f7d48e
   top_window_actor = get_top_visible_window_actor (compositor);
f7d48e
 
f7d48e
   if (priv->top_window_actor == top_window_actor)
f7d48e
diff --git a/src/compositor/meta-compositor-x11.c b/src/compositor/meta-compositor-x11.c
f7d48e
index 1d0ba4c8d8..afbe3f57e2 100644
f7d48e
--- a/src/compositor/meta-compositor-x11.c
f7d48e
+++ b/src/compositor/meta-compositor-x11.c
f7d48e
@@ -31,6 +31,8 @@
f7d48e
 #include "compositor/meta-sync-ring.h"
f7d48e
 #include "compositor/meta-window-actor-x11.h"
f7d48e
 #include "core/display-private.h"
f7d48e
+#include "core/stack-tracker.h"
f7d48e
+#include "core/stereo.h"
f7d48e
 #include "x11/meta-x11-display-private.h"
f7d48e
 
f7d48e
 struct _MetaCompositorX11
f7d48e
@@ -50,8 +52,24 @@ struct _MetaCompositorX11
f7d48e
   gboolean xserver_uses_monotonic_clock;
f7d48e
   int64_t xserver_time_query_time_us;
f7d48e
   int64_t xserver_time_offset_us;
f7d48e
+
f7d48e
+  int glx_opcode;
f7d48e
+  gboolean stereo_tree_ext;
f7d48e
+  gboolean have_stereo_windows;
f7d48e
 };
f7d48e
 
f7d48e
+typedef struct
f7d48e
+{
f7d48e
+  int type;
f7d48e
+  unsigned long serial;
f7d48e
+  Bool send_event;
f7d48e
+  Display *display;
f7d48e
+  int extension;
f7d48e
+  int evtype;
f7d48e
+  Drawable window;
f7d48e
+  Bool stereo_tree;
f7d48e
+} StereoNotifyEvent;
f7d48e
+
f7d48e
 G_DEFINE_TYPE (MetaCompositorX11, meta_compositor_x11, META_TYPE_COMPOSITOR)
f7d48e
 
f7d48e
 static void
f7d48e
@@ -95,6 +113,27 @@ meta_compositor_x11_process_xevent (MetaCompositorX11 *compositor_x11,
f7d48e
       if (window)
f7d48e
         process_damage (compositor_x11, (XDamageNotifyEvent *) xevent, window);
f7d48e
     }
f7d48e
+  else if (xevent->type == GenericEvent &&
f7d48e
+           xevent->xcookie.extension == compositor_x11->glx_opcode)
f7d48e
+    {
f7d48e
+      if (xevent->xcookie.evtype == GLX_STEREO_NOTIFY_EXT)
f7d48e
+        {
f7d48e
+          StereoNotifyEvent *stereo_event =
f7d48e
+            (StereoNotifyEvent *) (xevent->xcookie.data);
f7d48e
+
f7d48e
+          window = meta_x11_display_lookup_x_window (x11_display,
f7d48e
+                                                     stereo_event->window);
f7d48e
+          if (window)
f7d48e
+            {
f7d48e
+              MetaWindowActor *window_actor = meta_window_actor_from_window (window);
f7d48e
+              MetaDisplay *display = meta_window_get_display (window);
f7d48e
+
f7d48e
+              meta_window_actor_stereo_notify (window_actor,
f7d48e
+                                               stereo_event->stereo_tree);
f7d48e
+              meta_stack_tracker_queue_sync_stack (display->stack_tracker);
f7d48e
+            }
f7d48e
+        }
f7d48e
+    }
f7d48e
 
f7d48e
   if (compositor_x11->have_x11_sync_object)
f7d48e
     meta_sync_ring_handle_event (xevent);
f7d48e
@@ -107,6 +146,85 @@ meta_compositor_x11_process_xevent (MetaCompositorX11 *compositor_x11,
f7d48e
     meta_x11_handle_event (xevent);
f7d48e
 }
f7d48e
 
f7d48e
+#define GLX_STEREO_TREE_EXT        0x20F5
f7d48e
+#define GLX_STEREO_NOTIFY_MASK_EXT 0x00000001
f7d48e
+#define GLX_STEREO_NOTIFY_EXT      0x00000000
f7d48e
+
f7d48e
+static gboolean
f7d48e
+display_has_stereo_tree_ext (MetaX11Display *x11_display)
f7d48e
+{
f7d48e
+  Display     *xdisplay = x11_display->xdisplay;
f7d48e
+  const char  *extensions_string;
f7d48e
+
f7d48e
+  static const char * (*query_extensions_string) (Display *display,
f7d48e
+                                                  int      screen);
f7d48e
+
f7d48e
+  if (query_extensions_string == NULL)
f7d48e
+    query_extensions_string =
f7d48e
+      (const char * (*) (Display *, int))
f7d48e
+      cogl_get_proc_address ("glXQueryExtensionsString");
f7d48e
+
f7d48e
+  extensions_string = query_extensions_string (xdisplay,
f7d48e
+                                               meta_x11_display_get_screen_number (x11_display));
f7d48e
+
f7d48e
+  return extensions_string && strstr (extensions_string, "EXT_stereo_tree") != 0;
f7d48e
+}
f7d48e
+
f7d48e
+#include <GL/gl.h>
f7d48e
+
f7d48e
+gboolean
f7d48e
+meta_compositor_x11_window_is_stereo (MetaCompositorX11 *compositor_x11,
f7d48e
+                                      Window             xwindow)
f7d48e
+{
f7d48e
+  MetaCompositor *compositor = META_COMPOSITOR (compositor_x11);
f7d48e
+  MetaDisplay *display = meta_compositor_get_display (compositor);
f7d48e
+  Display *xdisplay = meta_x11_display_get_xdisplay (display->x11_display);
f7d48e
+
f7d48e
+  static int (*query_drawable) (Display      *dpy,
f7d48e
+                                Drawable      draw,
f7d48e
+                                int           attribute,
f7d48e
+                                unsigned int *value);
f7d48e
+
f7d48e
+  if (compositor_x11->stereo_tree_ext)
f7d48e
+    {
f7d48e
+      unsigned int stereo_tree = 0;
f7d48e
+
f7d48e
+      if (query_drawable == NULL)
f7d48e
+        query_drawable =
f7d48e
+          (int (*) (Display *, Drawable, int, unsigned int *))
f7d48e
+          cogl_get_proc_address ("glXQueryDrawable");
f7d48e
+
f7d48e
+      query_drawable (xdisplay, xwindow, GLX_STEREO_TREE_EXT, &stereo_tree);
f7d48e
+
f7d48e
+      return stereo_tree != 0;
f7d48e
+    }
f7d48e
+  else
f7d48e
+    return FALSE;
f7d48e
+}
f7d48e
+
f7d48e
+void
f7d48e
+meta_compositor_x11_select_stereo_notify (MetaCompositorX11 *compositor_x11,
f7d48e
+                                          Window             xwindow)
f7d48e
+{
f7d48e
+  MetaCompositor *compositor = META_COMPOSITOR (compositor_x11);
f7d48e
+  MetaDisplay *display = meta_compositor_get_display (compositor);
f7d48e
+  Display *xdisplay = meta_x11_display_get_xdisplay (display->x11_display);
f7d48e
+
f7d48e
+  static void (*select_event) (Display      *dpy,
f7d48e
+                               Drawable      draw,
f7d48e
+                               unsigned long event_mask);
f7d48e
+
f7d48e
+  if (compositor_x11->stereo_tree_ext)
f7d48e
+    {
f7d48e
+      if (select_event == NULL)
f7d48e
+        select_event =
f7d48e
+          (void (*) (Display *, Drawable, unsigned long))
f7d48e
+          cogl_get_proc_address ("glXSelectEvent");
f7d48e
+
f7d48e
+      select_event (xdisplay, xwindow, GLX_STEREO_NOTIFY_MASK_EXT);
f7d48e
+    }
f7d48e
+}
f7d48e
+
f7d48e
 static void
f7d48e
 determine_server_clock_source (MetaCompositorX11 *compositor_x11)
f7d48e
 {
f7d48e
@@ -142,6 +260,7 @@ meta_compositor_x11_manage (MetaCompositor  *compositor,
f7d48e
   MetaX11Display *x11_display = display->x11_display;
f7d48e
   Display *xdisplay = meta_x11_display_get_xdisplay (x11_display);
f7d48e
   int composite_version;
f7d48e
+  int glx_major_opcode, glx_first_event, glx_first_error;
f7d48e
   MetaBackend *backend = meta_get_backend ();
f7d48e
   Window xwindow;
f7d48e
 
f7d48e
@@ -166,10 +285,18 @@ meta_compositor_x11_manage (MetaCompositor  *compositor,
f7d48e
       return FALSE;
f7d48e
     }
f7d48e
 
f7d48e
+  if (XQueryExtension (xdisplay,
f7d48e
+                       "GLX",
f7d48e
+                       &glx_major_opcode, &glx_first_event, &glx_first_error))
f7d48e
+    compositor_x11->glx_opcode = glx_major_opcode;
f7d48e
+
f7d48e
   determine_server_clock_source (compositor_x11);
f7d48e
 
f7d48e
   meta_x11_display_set_cm_selection (display->x11_display);
f7d48e
 
f7d48e
+  compositor_x11->stereo_tree_ext =
f7d48e
+    display_has_stereo_tree_ext (display->x11_display);
f7d48e
+
f7d48e
   compositor_x11->output = display->x11_display->composite_overlay_window;
f7d48e
 
f7d48e
   xwindow = meta_backend_x11_get_xwindow (META_BACKEND_X11 (backend));
f7d48e
diff --git a/src/compositor/meta-compositor-x11.h b/src/compositor/meta-compositor-x11.h
f7d48e
index 42554feb39..61f3cd5950 100644
f7d48e
--- a/src/compositor/meta-compositor-x11.h
f7d48e
+++ b/src/compositor/meta-compositor-x11.h
f7d48e
@@ -36,4 +36,10 @@ void meta_compositor_x11_process_xevent (MetaCompositorX11 *compositor_x11,
f7d48e
 
f7d48e
 Window meta_compositor_x11_get_output_xwindow (MetaCompositorX11 *compositor_x11);
f7d48e
 
f7d48e
+gboolean meta_compositor_x11_window_is_stereo (MetaCompositorX11 *compositor_x11,
f7d48e
+                                               Window             xwindow);
f7d48e
+
f7d48e
+void meta_compositor_x11_select_stereo_notify (MetaCompositorX11 *compositor_x11,
f7d48e
+                                               Window             xwindow);
f7d48e
+
f7d48e
 #endif /* META_COMPOSITOR_X11_H */
f7d48e
diff --git a/src/compositor/meta-shaped-texture-private.h b/src/compositor/meta-shaped-texture-private.h
f7d48e
index 2fe1b8ea48..fadad07d69 100644
f7d48e
--- a/src/compositor/meta-shaped-texture-private.h
f7d48e
+++ b/src/compositor/meta-shaped-texture-private.h
f7d48e
@@ -31,8 +31,9 @@
f7d48e
 #include "meta/meta-shaped-texture.h"
f7d48e
 
f7d48e
 MetaShapedTexture *meta_shaped_texture_new (void);
f7d48e
-void meta_shaped_texture_set_texture (MetaShapedTexture *stex,
f7d48e
-                                      CoglTexture       *texture);
f7d48e
+void meta_shaped_texture_set_textures (MetaShapedTexture *stex,
f7d48e
+                                       CoglTexture       *texture,
f7d48e
+                                       CoglTexture       *texture_right);
f7d48e
 void meta_shaped_texture_set_is_y_inverted (MetaShapedTexture *stex,
f7d48e
                                             gboolean           is_y_inverted);
f7d48e
 void meta_shaped_texture_set_snippet (MetaShapedTexture *stex,
f7d48e
diff --git a/src/compositor/meta-shaped-texture.c b/src/compositor/meta-shaped-texture.c
f7d48e
index 6a8af828f0..43170a195d 100644
f7d48e
--- a/src/compositor/meta-shaped-texture.c
f7d48e
+++ b/src/compositor/meta-shaped-texture.c
f7d48e
@@ -83,8 +83,10 @@ struct _MetaShapedTexture
f7d48e
   GObject parent;
f7d48e
 
f7d48e
   MetaTextureTower *paint_tower;
f7d48e
+  MetaTextureTower *paint_tower_right;
f7d48e
 
f7d48e
   CoglTexture *texture;
f7d48e
+  CoglTexture *texture_right;
f7d48e
   CoglTexture *mask_texture;
f7d48e
   CoglSnippet *snippet;
f7d48e
 
f7d48e
@@ -151,6 +153,7 @@ static void
f7d48e
 meta_shaped_texture_init (MetaShapedTexture *stex)
f7d48e
 {
f7d48e
   stex->paint_tower = meta_texture_tower_new ();
f7d48e
+  stex->paint_tower_right = NULL;
f7d48e
 
f7d48e
   stex->buffer_scale = 1;
f7d48e
   stex->texture = NULL;
f7d48e
@@ -251,11 +254,11 @@ meta_shaped_texture_dispose (GObject *object)
f7d48e
 
f7d48e
   g_clear_handle_id (&stex->remipmap_timeout_id, g_source_remove);
f7d48e
 
f7d48e
-  if (stex->paint_tower)
f7d48e
-    meta_texture_tower_free (stex->paint_tower);
f7d48e
-  stex->paint_tower = NULL;
f7d48e
+  g_clear_pointer (&stex->paint_tower, meta_texture_tower_free);
f7d48e
+  g_clear_pointer (&stex->paint_tower_right, meta_texture_tower_free);
f7d48e
 
f7d48e
   g_clear_pointer (&stex->texture, cogl_object_unref);
f7d48e
+  g_clear_pointer (&stex->texture_right, cogl_object_unref);
f7d48e
 
f7d48e
   meta_shaped_texture_set_mask_texture (stex, NULL);
f7d48e
   meta_shaped_texture_reset_pipelines (stex);
f7d48e
@@ -521,14 +524,19 @@ paint_clipped_rectangle_node (MetaShapedTexture     *stex,
f7d48e
 }
f7d48e
 
f7d48e
 static void
f7d48e
-set_cogl_texture (MetaShapedTexture *stex,
f7d48e
-                  CoglTexture       *cogl_tex)
f7d48e
+set_cogl_textures (MetaShapedTexture *stex,
f7d48e
+                   CoglTexture       *cogl_tex,
f7d48e
+                   CoglTexture       *cogl_tex_right)
f7d48e
 {
f7d48e
   int width, height;
f7d48e
 
f7d48e
   cogl_clear_object (&stex->texture);
f7d48e
+  cogl_clear_object (&stex->texture_right);
f7d48e
 
f7d48e
-  if (cogl_tex != NULL)
f7d48e
+  stex->texture = cogl_tex;
f7d48e
+  stex->texture_right = cogl_tex_right;
f7d48e
+
f7d48e
+  if (cogl_tex)
f7d48e
     {
f7d48e
       stex->texture = cogl_object_ref (cogl_tex);
f7d48e
       width = cogl_texture_get_width (COGL_TEXTURE (cogl_tex));
f7d48e
@@ -540,6 +548,9 @@ set_cogl_texture (MetaShapedTexture *stex,
f7d48e
       height = 0;
f7d48e
     }
f7d48e
 
f7d48e
+  if (cogl_tex_right)
f7d48e
+    cogl_object_ref (cogl_tex_right);
f7d48e
+
f7d48e
   if (stex->tex_width != width ||
f7d48e
       stex->tex_height != height)
f7d48e
     {
f7d48e
@@ -553,8 +564,23 @@ set_cogl_texture (MetaShapedTexture *stex,
f7d48e
    * previous buffer. We only queue a redraw in response to surface
f7d48e
    * damage. */
f7d48e
 
f7d48e
+  if (cogl_tex_right)
f7d48e
+    {
f7d48e
+      if (!stex->paint_tower_right)
f7d48e
+        stex->paint_tower_right = meta_texture_tower_new ();
f7d48e
+    }
f7d48e
+  else
f7d48e
+    {
f7d48e
+      g_clear_pointer (&stex->paint_tower_right, meta_texture_tower_free);
f7d48e
+    }
f7d48e
+
f7d48e
   if (stex->create_mipmaps)
f7d48e
-    meta_texture_tower_set_base_texture (stex->paint_tower, cogl_tex);
f7d48e
+    {
f7d48e
+      meta_texture_tower_set_base_texture (stex->paint_tower, cogl_tex);
f7d48e
+
f7d48e
+      if (stex->paint_tower_right)
f7d48e
+        meta_texture_tower_set_base_texture (stex->paint_tower_right, cogl_tex_right);
f7d48e
+    }
f7d48e
 }
f7d48e
 
f7d48e
 static gboolean
f7d48e
@@ -582,6 +608,19 @@ flip_ints (int *x,
f7d48e
   *y = tmp;
f7d48e
 }
f7d48e
 
f7d48e
+static CoglFramebuffer *
f7d48e
+get_target_framebuffer (ClutterPaintNode    *root_node,
f7d48e
+                        ClutterPaintContext *paint_context)
f7d48e
+{
f7d48e
+  CoglFramebuffer *framebuffer;
f7d48e
+
f7d48e
+  framebuffer = clutter_paint_node_get_framebuffer (root_node);
f7d48e
+  if (!framebuffer)
f7d48e
+    framebuffer = clutter_paint_context_get_framebuffer (paint_context);
f7d48e
+
f7d48e
+  return framebuffer;
f7d48e
+}
f7d48e
+
f7d48e
 static void
f7d48e
 do_paint_content (MetaShapedTexture   *stex,
f7d48e
                   ClutterPaintNode    *root_node,
f7d48e
@@ -622,9 +661,7 @@ do_paint_content (MetaShapedTexture   *stex,
f7d48e
    * improves performance, especially with software rendering.
f7d48e
    */
f7d48e
 
f7d48e
-  framebuffer = clutter_paint_node_get_framebuffer (root_node);
f7d48e
-  if (!framebuffer)
f7d48e
-    framebuffer = clutter_paint_context_get_framebuffer (paint_context);
f7d48e
+  framebuffer = get_target_framebuffer (root_node, paint_context);
f7d48e
 
f7d48e
   if (stex->has_viewport_src_rect)
f7d48e
     {
f7d48e
@@ -826,13 +863,27 @@ do_paint_content (MetaShapedTexture   *stex,
f7d48e
 
f7d48e
 static CoglTexture *
f7d48e
 select_texture_for_paint (MetaShapedTexture   *stex,
f7d48e
-                          ClutterPaintContext *paint_context)
f7d48e
+                          ClutterPaintContext *paint_context,
f7d48e
+                          CoglStereoMode       stereo_mode)
f7d48e
 {
f7d48e
   CoglTexture *texture = NULL;
f7d48e
   int64_t now;
f7d48e
+  gboolean use_right_texture = FALSE;
f7d48e
 
f7d48e
-  if (!stex->texture)
f7d48e
-    return NULL;
f7d48e
+  switch (stereo_mode)
f7d48e
+    {
f7d48e
+    case COGL_STEREO_LEFT:
f7d48e
+    case COGL_STEREO_BOTH:
f7d48e
+      if (!stex->texture)
f7d48e
+        return NULL;
f7d48e
+      use_right_texture = FALSE;
f7d48e
+      break;
f7d48e
+    case COGL_STEREO_RIGHT:
f7d48e
+      if (!stex->texture_right)
f7d48e
+        return NULL;
f7d48e
+      use_right_texture = TRUE;
f7d48e
+      break;
f7d48e
+    }
f7d48e
 
f7d48e
   now = g_get_monotonic_time ();
f7d48e
 
f7d48e
@@ -843,14 +894,24 @@ select_texture_for_paint (MetaShapedTexture   *stex,
f7d48e
       if (age >= MIN_MIPMAP_AGE_USEC ||
f7d48e
           stex->fast_updates < MIN_FAST_UPDATES_BEFORE_UNMIPMAP)
f7d48e
         {
f7d48e
-          texture = meta_texture_tower_get_paint_texture (stex->paint_tower,
f7d48e
+          MetaTextureTower *paint_tower;
f7d48e
+
f7d48e
+          if (use_right_texture)
f7d48e
+            paint_tower = stex->paint_tower_right;
f7d48e
+          else
f7d48e
+            paint_tower = stex->paint_tower;
f7d48e
+
f7d48e
+          texture = meta_texture_tower_get_paint_texture (paint_tower,
f7d48e
                                                           paint_context);
f7d48e
         }
f7d48e
     }
f7d48e
 
f7d48e
   if (!texture)
f7d48e
     {
f7d48e
-      texture = stex->texture;
f7d48e
+      if (use_right_texture)
f7d48e
+        texture = stex->texture_right;
f7d48e
+      else
f7d48e
+        texture = stex->texture;
f7d48e
 
f7d48e
       if (stex->create_mipmaps)
f7d48e
         {
f7d48e
@@ -876,35 +937,57 @@ meta_shaped_texture_paint_content (ClutterContent      *content,
f7d48e
 {
f7d48e
   MetaShapedTexture *stex = META_SHAPED_TEXTURE (content);
f7d48e
   ClutterActorBox alloc;
f7d48e
-  CoglTexture *paint_tex = NULL;
f7d48e
   uint8_t opacity;
f7d48e
+  CoglFramebuffer *framebuffer;
f7d48e
+  gboolean is_stereo;
f7d48e
 
f7d48e
   if (stex->clip_region && cairo_region_is_empty (stex->clip_region))
f7d48e
     return;
f7d48e
 
f7d48e
-  /* The GL EXT_texture_from_pixmap extension does allow for it to be
f7d48e
-   * used together with SGIS_generate_mipmap, however this is very
f7d48e
-   * rarely supported. Also, even when it is supported there
f7d48e
-   * are distinct performance implications from:
f7d48e
-   *
f7d48e
-   *  - Updating mipmaps that we don't need
f7d48e
-   *  - Having to reallocate pixmaps on the server into larger buffers
f7d48e
-   *
f7d48e
-   * So, we just unconditionally use our mipmap emulation code. If we
f7d48e
-   * wanted to use SGIS_generate_mipmap, we'd have to  query COGL to
f7d48e
-   * see if it was supported (no API currently), and then if and only
f7d48e
-   * if that was the case, set the clutter texture quality to HIGH.
f7d48e
-   * Setting the texture quality to high without SGIS_generate_mipmap
f7d48e
-   * support for TFP textures will result in fallbacks to XGetImage.
f7d48e
-   */
f7d48e
-  paint_tex = select_texture_for_paint (stex, paint_context);
f7d48e
-  if (!paint_tex)
f7d48e
+  if (!stex->texture)
f7d48e
     return;
f7d48e
 
f7d48e
   opacity = clutter_actor_get_paint_opacity (actor);
f7d48e
   clutter_actor_get_content_box (actor, &alloc);
f7d48e
 
f7d48e
-  do_paint_content (stex, root_node, paint_context, paint_tex, &alloc, opacity);
f7d48e
+  framebuffer = get_target_framebuffer (root_node, paint_context);
f7d48e
+  is_stereo = (stex->texture_right &&
f7d48e
+               cogl_framebuffer_get_is_stereo (framebuffer));
f7d48e
+
f7d48e
+  if (is_stereo)
f7d48e
+    {
f7d48e
+      CoglTexture *texture_left;
f7d48e
+      CoglTexture *texture_right;
f7d48e
+      g_autoptr (ClutterPaintNode) left_node = NULL;
f7d48e
+      g_autoptr (ClutterPaintNode) right_node = NULL;
f7d48e
+
f7d48e
+      texture_left = select_texture_for_paint (stex, paint_context,
f7d48e
+                                               COGL_STEREO_LEFT);
f7d48e
+      texture_right = select_texture_for_paint (stex, paint_context,
f7d48e
+                                                COGL_STEREO_RIGHT);
f7d48e
+
f7d48e
+      left_node = clutter_stereo_node_new (COGL_STEREO_LEFT);
f7d48e
+      clutter_paint_node_set_static_name (left_node, "MetaShapedTexture (left)");
f7d48e
+      right_node = clutter_stereo_node_new (COGL_STEREO_RIGHT);
f7d48e
+      clutter_paint_node_set_static_name (right_node, "MetaShapedTexture (right)");
f7d48e
+
f7d48e
+      clutter_paint_node_add_child (root_node, left_node);
f7d48e
+      clutter_paint_node_add_child (root_node, right_node);
f7d48e
+
f7d48e
+      do_paint_content (stex, left_node, paint_context,
f7d48e
+                        texture_left, &alloc, opacity);
f7d48e
+      do_paint_content (stex, right_node, paint_context,
f7d48e
+                        texture_right, &alloc, opacity);
f7d48e
+    }
f7d48e
+  else
f7d48e
+    {
f7d48e
+      CoglTexture *texture;
f7d48e
+
f7d48e
+      texture = select_texture_for_paint (stex, paint_context,
f7d48e
+                                          COGL_STEREO_BOTH);
f7d48e
+      do_paint_content (stex, root_node, paint_context,
f7d48e
+                        texture, &alloc, opacity);
f7d48e
+    }
f7d48e
 }
f7d48e
 
f7d48e
 static gboolean
f7d48e
@@ -946,6 +1029,12 @@ meta_shaped_texture_set_create_mipmaps (MetaShapedTexture *stex,
f7d48e
       stex->create_mipmaps = create_mipmaps;
f7d48e
       base_texture = create_mipmaps ? stex->texture : NULL;
f7d48e
       meta_texture_tower_set_base_texture (stex->paint_tower, base_texture);
f7d48e
+
f7d48e
+      if (stex->paint_tower_right)
f7d48e
+        {
f7d48e
+          base_texture = create_mipmaps ? stex->texture_right : NULL;
f7d48e
+          meta_texture_tower_set_base_texture (stex->paint_tower_right, base_texture);
f7d48e
+        }
f7d48e
     }
f7d48e
 }
f7d48e
 
f7d48e
@@ -1079,6 +1168,14 @@ meta_shaped_texture_update_area (MetaShapedTexture     *stex,
f7d48e
                                   y,
f7d48e
                                   width,
f7d48e
                                   height);
f7d48e
+  if (stex->paint_tower_right)
f7d48e
+    {
f7d48e
+      meta_texture_tower_update_area (stex->paint_tower_right,
f7d48e
+                                      x,
f7d48e
+                                      y,
f7d48e
+                                      width,
f7d48e
+                                      height);
f7d48e
+    }
f7d48e
 
f7d48e
   stex->prev_invalidation = stex->last_invalidation;
f7d48e
   stex->last_invalidation = g_get_monotonic_time ();
f7d48e
@@ -1098,20 +1195,21 @@ meta_shaped_texture_update_area (MetaShapedTexture     *stex,
f7d48e
 }
f7d48e
 
f7d48e
 /**
f7d48e
- * meta_shaped_texture_set_texture:
f7d48e
+ * meta_shaped_texture_set_textures:
f7d48e
  * @stex: The #MetaShapedTexture
f7d48e
  * @pixmap: The #CoglTexture to display
f7d48e
  */
f7d48e
 void
f7d48e
-meta_shaped_texture_set_texture (MetaShapedTexture *stex,
f7d48e
-                                 CoglTexture       *texture)
f7d48e
+meta_shaped_texture_set_textures (MetaShapedTexture *stex,
f7d48e
+                                  CoglTexture       *texture,
f7d48e
+                                  CoglTexture       *texture_right)
f7d48e
 {
f7d48e
   g_return_if_fail (META_IS_SHAPED_TEXTURE (stex));
f7d48e
 
f7d48e
-  if (stex->texture == texture)
f7d48e
+  if (stex->texture == texture && stex->texture_right == texture_right)
f7d48e
     return;
f7d48e
 
f7d48e
-  set_cogl_texture (stex, texture);
f7d48e
+  set_cogl_textures (stex, texture, texture_right);
f7d48e
 }
f7d48e
 
f7d48e
 /**
f7d48e
diff --git a/src/compositor/meta-surface-actor-wayland.c b/src/compositor/meta-surface-actor-wayland.c
f7d48e
index a182ad8513..1ddc83db2b 100644
f7d48e
--- a/src/compositor/meta-surface-actor-wayland.c
f7d48e
+++ b/src/compositor/meta-surface-actor-wayland.c
f7d48e
@@ -148,7 +148,7 @@ meta_surface_actor_wayland_dispose (GObject *object)
f7d48e
 
f7d48e
   stex = meta_surface_actor_get_texture (META_SURFACE_ACTOR (self));
f7d48e
   if (stex)
f7d48e
-    meta_shaped_texture_set_texture (stex, NULL);
f7d48e
+    meta_shaped_texture_set_textures (stex, NULL, NULL);
f7d48e
 
f7d48e
   if (self->surface)
f7d48e
     {
f7d48e
diff --git a/src/compositor/meta-surface-actor-x11.c b/src/compositor/meta-surface-actor-x11.c
f7d48e
index 41ae2dffbc..c7c3d08c36 100644
f7d48e
--- a/src/compositor/meta-surface-actor-x11.c
f7d48e
+++ b/src/compositor/meta-surface-actor-x11.c
f7d48e
@@ -30,6 +30,7 @@
f7d48e
 #include <X11/extensions/Xcomposite.h>
f7d48e
 
f7d48e
 #include "cogl/winsys/cogl-texture-pixmap-x11.h"
f7d48e
+#include "compositor/meta-compositor-x11.h"
f7d48e
 #include "compositor/meta-cullable.h"
f7d48e
 #include "compositor/meta-shaped-texture-private.h"
f7d48e
 #include "compositor/meta-window-actor-private.h"
f7d48e
@@ -47,6 +48,7 @@ struct _MetaSurfaceActorX11
f7d48e
   MetaDisplay *display;
f7d48e
 
f7d48e
   CoglTexture *texture;
f7d48e
+  CoglTexture *texture_right;
f7d48e
   Pixmap pixmap;
f7d48e
   Damage damage;
f7d48e
 
f7d48e
@@ -62,6 +64,8 @@ struct _MetaSurfaceActorX11
f7d48e
   guint size_changed : 1;
f7d48e
 
f7d48e
   guint unredirected   : 1;
f7d48e
+
f7d48e
+  guint stereo : 1;
f7d48e
 };
f7d48e
 
f7d48e
 G_DEFINE_TYPE (MetaSurfaceActorX11,
f7d48e
@@ -101,7 +105,7 @@ detach_pixmap (MetaSurfaceActorX11 *self)
f7d48e
    * you are supposed to be able to free a GLXPixmap after freeing the underlying
f7d48e
    * pixmap, but it certainly doesn't work with current DRI/Mesa
f7d48e
    */
f7d48e
-  meta_shaped_texture_set_texture (stex, NULL);
f7d48e
+  meta_shaped_texture_set_textures (stex, NULL, NULL);
f7d48e
   cogl_flush ();
f7d48e
 
f7d48e
   meta_x11_error_trap_push (display->x11_display);
f7d48e
@@ -110,6 +114,7 @@ detach_pixmap (MetaSurfaceActorX11 *self)
f7d48e
   meta_x11_error_trap_pop (display->x11_display);
f7d48e
 
f7d48e
   g_clear_pointer (&self->texture, cogl_object_unref);
f7d48e
+  g_clear_pointer (&self->texture_right, cogl_object_unref);
f7d48e
 }
f7d48e
 
f7d48e
 static void
f7d48e
@@ -119,23 +124,37 @@ set_pixmap (MetaSurfaceActorX11 *self,
f7d48e
   CoglContext *ctx = clutter_backend_get_cogl_context (clutter_get_default_backend ());
f7d48e
   MetaShapedTexture *stex = meta_surface_actor_get_texture (META_SURFACE_ACTOR (self));
f7d48e
   GError *error = NULL;
f7d48e
-  CoglTexture *texture;
f7d48e
+  CoglTexturePixmapX11 *texture;
f7d48e
+  CoglTexturePixmapX11 *texture_right;
f7d48e
 
f7d48e
   g_assert (self->pixmap == None);
f7d48e
   self->pixmap = pixmap;
f7d48e
 
f7d48e
-  texture = COGL_TEXTURE (cogl_texture_pixmap_x11_new (ctx, self->pixmap, FALSE, &error));
f7d48e
+  if (self->stereo)
f7d48e
+    texture = cogl_texture_pixmap_x11_new_left (ctx, pixmap, FALSE, &error);
f7d48e
+  else
f7d48e
+    texture = cogl_texture_pixmap_x11_new (ctx, pixmap, FALSE, &error);
f7d48e
+
f7d48e
+  if (self->stereo)
f7d48e
+    texture_right = cogl_texture_pixmap_x11_new_right (texture);
f7d48e
+  else
f7d48e
+    texture_right = NULL;
f7d48e
 
f7d48e
   if (error != NULL)
f7d48e
     {
f7d48e
       g_warning ("Failed to allocate stex texture: %s", error->message);
f7d48e
       g_error_free (error);
f7d48e
     }
f7d48e
-  else if (G_UNLIKELY (!cogl_texture_pixmap_x11_is_using_tfp_extension (COGL_TEXTURE_PIXMAP_X11 (texture))))
f7d48e
+  else if (G_UNLIKELY (!cogl_texture_pixmap_x11_is_using_tfp_extension (texture)))
f7d48e
     g_warning ("NOTE: Not using GLX TFP!");
f7d48e
 
f7d48e
-  self->texture = texture;
f7d48e
-  meta_shaped_texture_set_texture (stex, texture);
f7d48e
+  self->texture = COGL_TEXTURE (texture);
f7d48e
+  if (self->stereo)
f7d48e
+    self->texture_right = COGL_TEXTURE (texture_right);
f7d48e
+
f7d48e
+  meta_shaped_texture_set_textures (stex,
f7d48e
+                                    COGL_TEXTURE (texture),
f7d48e
+                                    COGL_TEXTURE (texture_right));;
f7d48e
 }
f7d48e
 
f7d48e
 static void
f7d48e
@@ -372,8 +391,8 @@ reset_texture (MetaSurfaceActorX11 *self)
f7d48e
   /* Setting the texture to NULL will cause all the FBO's cached by the
f7d48e
    * shaped texture's MetaTextureTower to be discarded and recreated.
f7d48e
    */
f7d48e
-  meta_shaped_texture_set_texture (stex, NULL);
f7d48e
-  meta_shaped_texture_set_texture (stex, self->texture);
f7d48e
+  meta_shaped_texture_set_textures (stex, NULL, NULL);
f7d48e
+  meta_shaped_texture_set_textures (stex, self->texture, self->texture_right);
f7d48e
 }
f7d48e
 
f7d48e
 MetaSurfaceActor *
f7d48e
@@ -381,12 +400,18 @@ meta_surface_actor_x11_new (MetaWindow *window)
f7d48e
 {
f7d48e
   MetaSurfaceActorX11 *self = g_object_new (META_TYPE_SURFACE_ACTOR_X11, NULL);
f7d48e
   MetaDisplay *display = meta_window_get_display (window);
f7d48e
+  MetaCompositorX11 *compositor_x11 = META_COMPOSITOR_X11 (display->compositor);
f7d48e
+  Window xwindow;
f7d48e
 
f7d48e
   g_assert (!meta_is_wayland_compositor ());
f7d48e
 
f7d48e
   self->window = window;
f7d48e
   self->display = display;
f7d48e
 
f7d48e
+  xwindow = meta_window_x11_get_toplevel_xwindow (window);
f7d48e
+  self->stereo = meta_compositor_x11_window_is_stereo (compositor_x11, xwindow);
f7d48e
+  meta_compositor_x11_select_stereo_notify (compositor_x11, xwindow);
f7d48e
+
f7d48e
   g_signal_connect_object (self->display, "gl-video-memory-purged",
f7d48e
                            G_CALLBACK (reset_texture), self, G_CONNECT_SWAPPED);
f7d48e
 
f7d48e
@@ -420,3 +445,17 @@ meta_surface_actor_x11_set_size (MetaSurfaceActorX11 *self,
f7d48e
   self->last_height = height;
f7d48e
   meta_shaped_texture_set_fallback_size (stex, width, height);
f7d48e
 }
f7d48e
+
f7d48e
+void
f7d48e
+meta_surface_actor_x11_stereo_notify (MetaSurfaceActorX11 *self,
f7d48e
+                                      gboolean             stereo_tree)
f7d48e
+{
f7d48e
+  self->stereo = stereo_tree != FALSE;
f7d48e
+  detach_pixmap (self);
f7d48e
+}
f7d48e
+
f7d48e
+gboolean
f7d48e
+meta_surface_actor_x11_is_stereo (MetaSurfaceActorX11 *self)
f7d48e
+{
f7d48e
+  return self->stereo;
f7d48e
+}
f7d48e
diff --git a/src/compositor/meta-surface-actor-x11.h b/src/compositor/meta-surface-actor-x11.h
f7d48e
index 0a8517236a..369f631ae0 100644
f7d48e
--- a/src/compositor/meta-surface-actor-x11.h
f7d48e
+++ b/src/compositor/meta-surface-actor-x11.h
f7d48e
@@ -57,6 +57,11 @@ gboolean meta_surface_actor_x11_is_visible (MetaSurfaceActorX11 *self);
f7d48e
 
f7d48e
 void meta_surface_actor_x11_handle_updates (MetaSurfaceActorX11 *self);
f7d48e
 
f7d48e
+void meta_surface_actor_x11_stereo_notify (MetaSurfaceActorX11 *self,
f7d48e
+                                           gboolean             stereo_tree);
f7d48e
+
f7d48e
+gboolean meta_surface_actor_x11_is_stereo (MetaSurfaceActorX11 *self);
f7d48e
+
f7d48e
 G_END_DECLS
f7d48e
 
f7d48e
 #endif /* __META_SURFACE_ACTOR_X11_H__ */
f7d48e
diff --git a/src/compositor/meta-window-actor-private.h b/src/compositor/meta-window-actor-private.h
f7d48e
index 64741e4167..d498879902 100644
f7d48e
--- a/src/compositor/meta-window-actor-private.h
f7d48e
+++ b/src/compositor/meta-window-actor-private.h
f7d48e
@@ -99,4 +99,9 @@ void meta_window_actor_update_regions (MetaWindowActor *self);
f7d48e
 
f7d48e
 gboolean meta_window_actor_can_freeze_commits (MetaWindowActor *self);
f7d48e
 
f7d48e
+void meta_window_actor_stereo_notify (MetaWindowActor *actor,
f7d48e
+                                      gboolean         stereo_tree);
f7d48e
+
f7d48e
+gboolean meta_window_actor_is_stereo (MetaWindowActor *actor);
f7d48e
+
f7d48e
 #endif /* META_WINDOW_ACTOR_PRIVATE_H */
f7d48e
diff --git a/src/compositor/meta-window-actor.c b/src/compositor/meta-window-actor.c
f7d48e
index d4fc9a43a0..56c85e1788 100644
f7d48e
--- a/src/compositor/meta-window-actor.c
f7d48e
+++ b/src/compositor/meta-window-actor.c
f7d48e
@@ -1557,3 +1557,25 @@ out:
f7d48e
   clutter_actor_uninhibit_culling (actor);
f7d48e
   return surface;
f7d48e
 }
f7d48e
+
f7d48e
+void
f7d48e
+meta_window_actor_stereo_notify (MetaWindowActor *self,
f7d48e
+                                 gboolean         stereo_tree)
f7d48e
+{
f7d48e
+  MetaWindowActorPrivate *priv = meta_window_actor_get_instance_private (self);
f7d48e
+
f7d48e
+  if (META_IS_SURFACE_ACTOR_X11 (priv->surface))
f7d48e
+    meta_surface_actor_x11_stereo_notify (META_SURFACE_ACTOR_X11 (priv->surface),
f7d48e
+                                          stereo_tree);
f7d48e
+}
f7d48e
+
f7d48e
+gboolean
f7d48e
+meta_window_actor_is_stereo (MetaWindowActor *self)
f7d48e
+{
f7d48e
+  MetaWindowActorPrivate *priv = meta_window_actor_get_instance_private (self);
f7d48e
+
f7d48e
+  if (META_IS_SURFACE_ACTOR_X11 (priv->surface))
f7d48e
+    return meta_surface_actor_x11_is_stereo (META_SURFACE_ACTOR_X11 (priv->surface));
f7d48e
+  else
f7d48e
+    return FALSE;
f7d48e
+}
f7d48e
diff --git a/src/core/main.c b/src/core/main.c
f7d48e
index 6dabcfe73e..a07dda9ecc 100644
f7d48e
--- a/src/core/main.c
f7d48e
+++ b/src/core/main.c
f7d48e
@@ -88,6 +88,7 @@
f7d48e
 #include "meta/meta-backend.h"
f7d48e
 #include "meta/meta-x11-errors.h"
f7d48e
 #include "meta/prefs.h"
f7d48e
+#include "stereo.h"
f7d48e
 #include "ui/ui.h"
f7d48e
 #include "x11/session.h"
f7d48e
 
f7d48e
@@ -848,6 +849,9 @@ meta_init (void)
f7d48e
   if (!meta_is_wayland_compositor ())
f7d48e
     meta_select_display (opt_display_name);
f7d48e
 
f7d48e
+  if (!meta_is_wayland_compositor ())
f7d48e
+    meta_stereo_init ();
f7d48e
+
f7d48e
   meta_init_backend (backend_gtype, n_properties, prop_names, prop_values);
f7d48e
 
f7d48e
   for (i = 0; i < n_properties; i++)
f7d48e
diff --git a/src/core/stereo.c b/src/core/stereo.c
f7d48e
new file mode 100644
f7d48e
index 0000000000..817056527f
f7d48e
--- /dev/null
f7d48e
+++ b/src/core/stereo.c
f7d48e
@@ -0,0 +1,154 @@
f7d48e
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
f7d48e
+
f7d48e
+/*
f7d48e
+ * Copyright (C) 2014 Red Hat, Inc.
f7d48e
+ *
f7d48e
+ * This program is free software; you can redistribute it and/or
f7d48e
+ * modify it under the terms of the GNU General Public License as
f7d48e
+ * published by the Free Software Foundation; either version 2 of the
f7d48e
+ * License, or (at your option) any later version.
f7d48e
+ *
f7d48e
+ * This program is distributed in the hope that it will be useful, but
f7d48e
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
f7d48e
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
f7d48e
+ * General Public License for more details.
f7d48e
+ *
f7d48e
+ * You should have received a copy of the GNU General Public License
f7d48e
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
f7d48e
+ */
f7d48e
+
f7d48e
+/*
f7d48e
+ * SECTION:stereo
f7d48e
+ * @short_description: Keep track of whether we are a stereo compositor
f7d48e
+ *
f7d48e
+ * With GLX, we need to use a different GL context for stereo and
f7d48e
+ * non-stereo support. Support for multiple GL contexts is unfinished
f7d48e
+ * in Cogl and entirely lacking in Clutter, so it's by far easier
f7d48e
+ * to just restart Mutter when we detect a stereo window.
f7d48e
+ *
f7d48e
+ * A property _MUTTER_ENABLE_STEREO is maintained on the root window
f7d48e
+ * to know whether we should initialize clutter for stereo or not.
f7d48e
+ * When the presence or absence of stereo windows mismatches the
f7d48e
+ * stereo-enabled state for a sufficiently long period of time,
f7d48e
+ * we restart Mutter.
f7d48e
+ */
f7d48e
+
f7d48e
+#include <config.h>
f7d48e
+
f7d48e
+#include <clutter/x11/clutter-x11.h>
f7d48e
+#include <gio/gunixinputstream.h>
f7d48e
+#include <X11/Xatom.h>
f7d48e
+
f7d48e
+#include "display-private.h"
f7d48e
+#include <meta/main.h>
f7d48e
+#include <meta/meta-x11-display.h>
f7d48e
+#include <meta/util.h>
f7d48e
+#include "stereo.h"
f7d48e
+#include "ui/ui.h"
f7d48e
+#include "util-private.h"
f7d48e
+
f7d48e
+static guint stereo_switch_id = 0;
f7d48e
+static gboolean stereo_enabled = FALSE;
f7d48e
+/* -1 so the first time meta_stereo_set_have_stereo_windows() is called
f7d48e
+ * we avoid the short-circuit and set up a timeout to restart
f7d48e
+ * if necessary */
f7d48e
+static gboolean stereo_have_windows = (gboolean)-1;
f7d48e
+static gboolean stereo_restart = FALSE;
f7d48e
+
f7d48e
+#define STEREO_ENABLE_WAIT 1000
f7d48e
+#define STEREO_DISABLE_WAIT 5000
f7d48e
+
f7d48e
+void
f7d48e
+meta_stereo_init (void)
f7d48e
+{
f7d48e
+  Display *xdisplay;
f7d48e
+  Window root;
f7d48e
+  Atom atom_enable_stereo;
f7d48e
+  Atom type;
f7d48e
+  int format;
f7d48e
+  unsigned long n_items, bytes_after;
f7d48e
+  guchar *data;
f7d48e
+
f7d48e
+  xdisplay = XOpenDisplay (NULL);
f7d48e
+  if (xdisplay == NULL)
f7d48e
+    meta_fatal ("Unable to open X display %s\n", XDisplayName (NULL));
f7d48e
+
f7d48e
+  root = DefaultRootWindow (xdisplay);
f7d48e
+  atom_enable_stereo = XInternAtom (xdisplay, "_MUTTER_ENABLE_STEREO", False);
f7d48e
+
f7d48e
+  XGetWindowProperty (xdisplay, root, atom_enable_stereo,
f7d48e
+                      0, 1, False, XA_INTEGER,
f7d48e
+                      &type, &format, &n_items, &bytes_after, &data);
f7d48e
+  if (type == XA_INTEGER)
f7d48e
+    {
f7d48e
+      if (format == 32 && n_items == 1 && bytes_after == 0)
f7d48e
+        {
f7d48e
+          stereo_enabled = *(long *)data;
f7d48e
+        }
f7d48e
+      else
f7d48e
+        {
f7d48e
+          meta_warning ("Bad value for _MUTTER_ENABLE_STEREO property\n");
f7d48e
+        }
f7d48e
+
f7d48e
+      XFree (data);
f7d48e
+    }
f7d48e
+  else if (type != None)
f7d48e
+    {
f7d48e
+      meta_warning ("Bad type for _MUTTER_ENABLE_STEREO property\n");
f7d48e
+    }
f7d48e
+
f7d48e
+  meta_verbose ("On startup, _MUTTER_ENABLE_STEREO=%s",
f7d48e
+                stereo_enabled ? "yes" : "no");
f7d48e
+  clutter_x11_set_use_stereo_stage (stereo_enabled);
f7d48e
+  XCloseDisplay (xdisplay);
f7d48e
+}
f7d48e
+
f7d48e
+static gboolean
f7d48e
+meta_stereo_switch (gpointer data)
f7d48e
+{
f7d48e
+  stereo_switch_id = 0;
f7d48e
+  stereo_restart = TRUE;
f7d48e
+
f7d48e
+  meta_restart (stereo_have_windows ?
f7d48e
+                _("Enabling stereo...") :
f7d48e
+                _("Disabling stereo..."));
f7d48e
+
f7d48e
+  return FALSE;
f7d48e
+}
f7d48e
+
f7d48e
+void
f7d48e
+meta_stereo_set_have_stereo_windows (gboolean have_windows)
f7d48e
+{
f7d48e
+  have_windows = have_windows != FALSE;
f7d48e
+
f7d48e
+  if (!stereo_restart && have_windows != stereo_have_windows)
f7d48e
+    {
f7d48e
+      MetaDisplay *display = meta_get_display ();
f7d48e
+      Display *xdisplay = meta_x11_display_get_xdisplay (display->x11_display);
f7d48e
+      Window root = DefaultRootWindow (xdisplay);
f7d48e
+      Atom atom_enable_stereo = XInternAtom (xdisplay, "_MUTTER_ENABLE_STEREO", False);
f7d48e
+      long value;
f7d48e
+
f7d48e
+      stereo_have_windows = have_windows;
f7d48e
+
f7d48e
+      if (stereo_have_windows)
f7d48e
+        meta_verbose ("Detected stereo windows\n");
f7d48e
+      else
f7d48e
+        meta_verbose ("No stereo windows detected\n");
f7d48e
+
f7d48e
+      value = stereo_have_windows;
f7d48e
+      XChangeProperty (xdisplay, root,
f7d48e
+                       atom_enable_stereo, XA_INTEGER, 32,
f7d48e
+                       PropModeReplace, (guchar *)&value, 1);
f7d48e
+
f7d48e
+      if (stereo_switch_id != 0)
f7d48e
+        {
f7d48e
+          g_source_remove (stereo_switch_id);
f7d48e
+          stereo_switch_id = 0;
f7d48e
+        }
f7d48e
+
f7d48e
+      if (stereo_have_windows != stereo_enabled)
f7d48e
+        stereo_switch_id = g_timeout_add (stereo_have_windows ? STEREO_ENABLE_WAIT : STEREO_DISABLE_WAIT,
f7d48e
+                                          meta_stereo_switch, NULL);
f7d48e
+    }
f7d48e
+}
f7d48e
diff --git a/src/core/stereo.h b/src/core/stereo.h
f7d48e
new file mode 100644
f7d48e
index 0000000000..ccd1d702a1
f7d48e
--- /dev/null
f7d48e
+++ b/src/core/stereo.h
f7d48e
@@ -0,0 +1,28 @@
f7d48e
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
f7d48e
+
f7d48e
+/*
f7d48e
+ * Copyright (C) 2014 Red Hat, Inc.
f7d48e
+ *
f7d48e
+ * This program is free software; you can redistribute it and/or
f7d48e
+ * modify it under the terms of the GNU General Public License as
f7d48e
+ * published by the Free Software Foundation; either version 2 of the
f7d48e
+ * License, or (at your option) any later version.
f7d48e
+ *
f7d48e
+ * This program is distributed in the hope that it will be useful, but
f7d48e
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
f7d48e
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
f7d48e
+ * General Public License for more details.
f7d48e
+ *
f7d48e
+ * You should have received a copy of the GNU General Public License
f7d48e
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
f7d48e
+ */
f7d48e
+
f7d48e
+#ifndef META_STEREO_H
f7d48e
+#define META_STEREO_H
f7d48e
+
f7d48e
+void     meta_stereo_init                    (void);
f7d48e
+void     meta_stereo_set_have_stereo_windows (gboolean have_windows);
f7d48e
+gboolean meta_stereo_is_restart              (void);
f7d48e
+void     meta_stereo_finish_restart          (void);
f7d48e
+
f7d48e
+#endif
f7d48e
diff --git a/src/meson.build b/src/meson.build
f7d48e
index 284bdf5220..c56438fbbe 100644
f7d48e
--- a/src/meson.build
f7d48e
+++ b/src/meson.build
f7d48e
@@ -394,6 +394,8 @@ mutter_sources = [
f7d48e
   'core/stack.h',
f7d48e
   'core/stack-tracker.c',
f7d48e
   'core/stack-tracker.h',
f7d48e
+  'core/stereo.c',
f7d48e
+  'core/stereo.h',
f7d48e
   'core/startup-notification.c',
f7d48e
   'core/startup-notification-private.h',
f7d48e
   'core/util.c',
f7d48e
diff --git a/src/wayland/meta-wayland-actor-surface.c b/src/wayland/meta-wayland-actor-surface.c
f7d48e
index 797795f861..d8d1f3ce16 100644
f7d48e
--- a/src/wayland/meta-wayland-actor-surface.c
f7d48e
+++ b/src/wayland/meta-wayland-actor-surface.c
f7d48e
@@ -193,7 +193,7 @@ meta_wayland_actor_surface_real_sync_actor_state (MetaWaylandActorSurface *actor
f7d48e
       snippet = meta_wayland_buffer_create_snippet (buffer);
f7d48e
       is_y_inverted = meta_wayland_buffer_is_y_inverted (buffer);
f7d48e
 
f7d48e
-      meta_shaped_texture_set_texture (stex, surface->texture);
f7d48e
+      meta_shaped_texture_set_textures (stex, surface->texture, NULL);
f7d48e
       meta_shaped_texture_set_snippet (stex, snippet);
f7d48e
       meta_shaped_texture_set_is_y_inverted (stex, is_y_inverted);
f7d48e
       meta_shaped_texture_set_buffer_scale (stex, surface->scale);
f7d48e
@@ -201,7 +201,7 @@ meta_wayland_actor_surface_real_sync_actor_state (MetaWaylandActorSurface *actor
f7d48e
     }
f7d48e
   else
f7d48e
     {
f7d48e
-      meta_shaped_texture_set_texture (stex, NULL);
f7d48e
+      meta_shaped_texture_set_textures (stex, NULL, NULL);
f7d48e
     }
f7d48e
 
f7d48e
   surface_rect = (cairo_rectangle_int_t) {
f7d48e
-- 
f7d48e
2.31.1
f7d48e
f7d48e
f7d48e
From 36517cd245584c5bcd3b730efaf6c3d2e7c9472d Mon Sep 17 00:00:00 2001
f7d48e
From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
f7d48e
Date: Wed, 2 Jun 2021 16:55:45 +0200
f7d48e
Subject: [PATCH 2/2] compositor: Only check for stereo when using GLX
f7d48e
f7d48e
If EGL Xlib is used, we'll get bogus return value and crash.
f7d48e
---
f7d48e
 src/compositor/meta-compositor-x11.c | 8 ++++++++
f7d48e
 1 file changed, 8 insertions(+)
f7d48e
f7d48e
diff --git a/src/compositor/meta-compositor-x11.c b/src/compositor/meta-compositor-x11.c
f7d48e
index afbe3f57e2..4345f38b6f 100644
f7d48e
--- a/src/compositor/meta-compositor-x11.c
f7d48e
+++ b/src/compositor/meta-compositor-x11.c
f7d48e
@@ -153,9 +153,17 @@ meta_compositor_x11_process_xevent (MetaCompositorX11 *compositor_x11,
f7d48e
 static gboolean
f7d48e
 display_has_stereo_tree_ext (MetaX11Display *x11_display)
f7d48e
 {
f7d48e
+  MetaBackend *backend = meta_get_backend ();
f7d48e
+  ClutterBackend *clutter_backend = meta_backend_get_clutter_backend (backend);
f7d48e
+  CoglContext *cogl_context =
f7d48e
+    clutter_backend_get_cogl_context (clutter_backend);
f7d48e
+  CoglRenderer *cogl_renderer = cogl_context_get_renderer (cogl_context);
f7d48e
   Display     *xdisplay = x11_display->xdisplay;
f7d48e
   const char  *extensions_string;
f7d48e
 
f7d48e
+  if (cogl_renderer_get_winsys_id (cogl_renderer) != COGL_WINSYS_ID_GLX)
f7d48e
+    return FALSE;
f7d48e
+
f7d48e
   static const char * (*query_extensions_string) (Display *display,
f7d48e
                                                   int      screen);
f7d48e
 
f7d48e
-- 
f7d48e
2.31.1
f7d48e