Blame SOURCES/0001-Add-support-for-quad-buffer-stereo.patch

1c7749
From 2b45c3e37da7a590227e965d8a18e89337b791ba Mon Sep 17 00:00:00 2001
1c7749
From: "Owen W. Taylor" <otaylor@fishsoup.net>
1c7749
Date: Thu, 8 May 2014 18:44:15 -0400
1c7749
Subject: [PATCH] Add support for quad-buffer stereo
1c7749
1c7749
Track the stereo status of windows using the new EXT_stereo_tree
1c7749
GLX extension.
1c7749
1c7749
When stereo is enabled or disabled, a restart is triggered via
1c7749
meta_restart() after a timeout, setting a _META_ENABLE_STEREO
1c7749
property on the root window to indicate whether we should
1c7749
turn on a stereo stage for clutter. The property avoids a loop,
1c7749
since we need to enable stereo *before* initializing Clutter and GL,
1c7749
but we need GL to figure out whether we have stereo windows.
1c7749
1c7749
Stereo windows are drawn to the stage using new functionality
1c7749
in Cogl to setup a stereo context, select which buffer to draw
1c7749
to, and draw either the left or right buffer of a stereo
1c7749
texture_from_pixmap.
1c7749
---
1c7749
 src/compositor/compositor-private.h          |   9 ++
1c7749
 src/compositor/compositor.c                  | 125 +++++++++++++++
1c7749
 src/compositor/meta-shaped-texture-private.h |   5 +-
1c7749
 src/compositor/meta-shaped-texture.c         |  86 ++++++++++-
1c7749
 src/compositor/meta-surface-actor-wayland.c  |   2 +-
1c7749
 src/compositor/meta-surface-actor-x11.c      |  54 ++++++-
1c7749
 src/compositor/meta-surface-actor-x11.h      |   5 +
1c7749
 src/compositor/meta-window-actor-private.h   |   5 +
1c7749
 src/compositor/meta-window-actor.c           |  22 +++
1c7749
 src/core/main.c                              |   4 +
1c7749
 src/core/stereo.c                            | 154 +++++++++++++++++++
1c7749
 src/core/stereo.h                            |  28 ++++
1c7749
 src/meson.build                              |   2 +
1c7749
 src/wayland/meta-wayland-surface.c           |   2 +-
1c7749
 14 files changed, 483 insertions(+), 20 deletions(-)
1c7749
 create mode 100644 src/core/stereo.c
1c7749
 create mode 100644 src/core/stereo.h
1c7749
1c7749
diff --git a/src/compositor/compositor-private.h b/src/compositor/compositor-private.h
1c7749
index 6ab33416c..f70087512 100644
1c7749
--- a/src/compositor/compositor-private.h
1c7749
+++ b/src/compositor/compositor-private.h
1c7749
@@ -24,6 +24,10 @@ struct _MetaCompositor
1c7749
   gint64          server_time_query_time;
1c7749
   gint64          server_time_offset;
1c7749
 
1c7749
+  int             glx_opcode;
1c7749
+  guint           stereo_tree_ext : 1;
1c7749
+  guint           have_stereo_windows : 1;
1c7749
+
1c7749
   guint           server_time_is_monotonic_time : 1;
1c7749
 
1c7749
   ClutterActor          *stage, *window_group, *top_window_group, *feedback_group;
1c7749
@@ -63,6 +67,11 @@ void     meta_end_modal_for_plugin   (MetaCompositor   *compositor,
1c7749
 gint64 meta_compositor_monotonic_time_to_server_time (MetaDisplay *display,
1c7749
                                                       gint64       monotonic_time);
1c7749
 
1c7749
+gboolean meta_compositor_window_is_stereo     (MetaDisplay *display,
1c7749
+                                               Window       xwindow);
1c7749
+void     meta_compositor_select_stereo_notify (MetaDisplay *display,
1c7749
+                                               Window       xwindow);
1c7749
+
1c7749
 void meta_compositor_flash_window (MetaCompositor *compositor,
1c7749
                                    MetaWindow     *window);
1c7749
 
1c7749
diff --git a/src/compositor/compositor.c b/src/compositor/compositor.c
1c7749
index 2a2c8fb3b..6c08c8fe4 100644
1c7749
--- a/src/compositor/compositor.c
1c7749
+++ b/src/compositor/compositor.c
1c7749
@@ -69,6 +69,8 @@
1c7749
 #include "core/core.h"
1c7749
 #include "core/display-private.h"
1c7749
 #include "core/frame.h"
1c7749
+#include "core/stack-tracker.h"
1c7749
+#include "core/stereo.h"
1c7749
 #include "core/util-private.h"
1c7749
 #include "core/window-private.h"
1c7749
 #include "meta/compositor-mutter.h"
1c7749
@@ -514,6 +516,94 @@ redirect_windows (MetaX11Display *x11_display)
1c7749
     }
1c7749
 }
1c7749
 
1c7749
+#define GLX_STEREO_TREE_EXT        0x20F5
1c7749
+#define GLX_STEREO_NOTIFY_MASK_EXT 0x00000001
1c7749
+#define GLX_STEREO_NOTIFY_EXT      0x00000000
1c7749
+
1c7749
+typedef struct {
1c7749
+  int type;
1c7749
+  unsigned long serial;
1c7749
+  Bool send_event;
1c7749
+  Display *display;
1c7749
+  int extension;
1c7749
+  int evtype;
1c7749
+  Drawable window;
1c7749
+  Bool stereo_tree;
1c7749
+} StereoNotifyEvent;
1c7749
+
1c7749
+static gboolean
1c7749
+display_has_stereo_tree_ext (MetaX11Display *x11_display)
1c7749
+{
1c7749
+  Display     *xdisplay = x11_display->xdisplay;
1c7749
+  const char  *extensions_string;
1c7749
+
1c7749
+  static const char * (*query_extensions_string) (Display *display,
1c7749
+                                                  int      screen);
1c7749
+
1c7749
+  if (query_extensions_string == NULL)
1c7749
+    query_extensions_string =
1c7749
+      (const char * (*) (Display *, int))
1c7749
+      cogl_get_proc_address ("glXQueryExtensionsString");
1c7749
+
1c7749
+  extensions_string = query_extensions_string (xdisplay,
1c7749
+                                               meta_x11_display_get_screen_number (x11_display));
1c7749
+
1c7749
+  return extensions_string && strstr (extensions_string, "EXT_stereo_tree") != 0;
1c7749
+}
1c7749
+
1c7749
+#include <GL/gl.h>
1c7749
+
1c7749
+gboolean
1c7749
+meta_compositor_window_is_stereo (MetaDisplay *display,
1c7749
+                                  Window       xwindow)
1c7749
+{
1c7749
+  MetaCompositor *compositor = get_compositor_for_display (display);
1c7749
+  Display *xdisplay = meta_x11_display_get_xdisplay (display->x11_display);
1c7749
+
1c7749
+  static int (*query_drawable) (Display      *dpy,
1c7749
+                                Drawable      draw,
1c7749
+                                int           attribute,
1c7749
+                                unsigned int *value);
1c7749
+
1c7749
+  if (compositor->stereo_tree_ext)
1c7749
+    {
1c7749
+      unsigned int stereo_tree = 0;
1c7749
+
1c7749
+      if (query_drawable == NULL)
1c7749
+        query_drawable =
1c7749
+          (int (*) (Display *, Drawable, int, unsigned int *))
1c7749
+          cogl_get_proc_address ("glXQueryDrawable");
1c7749
+
1c7749
+      query_drawable (xdisplay, xwindow, GLX_STEREO_TREE_EXT, &stereo_tree);
1c7749
+
1c7749
+      return stereo_tree != 0;
1c7749
+    }
1c7749
+  else
1c7749
+    return FALSE;
1c7749
+}
1c7749
+
1c7749
+void
1c7749
+meta_compositor_select_stereo_notify (MetaDisplay *display,
1c7749
+                                      Window       xwindow)
1c7749
+{
1c7749
+  MetaCompositor *compositor = get_compositor_for_display (display);
1c7749
+  Display *xdisplay = meta_x11_display_get_xdisplay (display->x11_display);
1c7749
+
1c7749
+  static void (*select_event) (Display      *dpy,
1c7749
+                               Drawable      draw,
1c7749
+                               unsigned long event_mask);
1c7749
+
1c7749
+  if (compositor->stereo_tree_ext)
1c7749
+    {
1c7749
+      if (select_event == NULL)
1c7749
+        select_event =
1c7749
+          (void (*) (Display *, Drawable, unsigned long))
1c7749
+          cogl_get_proc_address ("glXSelectEvent");
1c7749
+
1c7749
+      select_event (xdisplay, xwindow, GLX_STEREO_NOTIFY_MASK_EXT);
1c7749
+    }
1c7749
+}
1c7749
+
1c7749
 void
1c7749
 meta_compositor_manage (MetaCompositor *compositor)
1c7749
 {
1c7749
@@ -525,6 +615,8 @@ meta_compositor_manage (MetaCompositor *compositor)
1c7749
     {
1c7749
       xdisplay = display->x11_display->xdisplay;
1c7749
       meta_x11_display_set_cm_selection (display->x11_display);
1c7749
+
1c7749
+      compositor->stereo_tree_ext = display_has_stereo_tree_ext (display->x11_display);
1c7749
     }
1c7749
 
1c7749
   compositor->stage = meta_backend_get_stage (backend);
1c7749
@@ -822,6 +914,23 @@ meta_compositor_process_event (MetaCompositor *compositor,
1c7749
       if (window)
1c7749
         process_damage (compositor, (XDamageNotifyEvent *) event, window);
1c7749
     }
1c7749
+  else if (!meta_is_wayland_compositor () &&
1c7749
+           event->type == GenericEvent &&
1c7749
+           event->xcookie.extension == compositor->glx_opcode)
1c7749
+    {
1c7749
+      if (event->xcookie.evtype == GLX_STEREO_NOTIFY_EXT)
1c7749
+        {
1c7749
+          StereoNotifyEvent *stereo_event = (StereoNotifyEvent *)(event->xcookie.data);
1c7749
+          window = meta_x11_display_lookup_x_window (x11_display, stereo_event->window);
1c7749
+
1c7749
+          if (window != NULL)
1c7749
+            {
1c7749
+              MetaWindowActor *window_actor = META_WINDOW_ACTOR (meta_window_get_compositor_private (window));
1c7749
+              meta_window_actor_stereo_notify (window_actor, stereo_event->stereo_tree);
1c7749
+              meta_stack_tracker_queue_sync_stack (window->display->stack_tracker);
1c7749
+            }
1c7749
+        }
1c7749
+    }
1c7749
 
1c7749
   if (compositor->have_x11_sync_object)
1c7749
     meta_sync_ring_handle_event (event);
1c7749
@@ -1038,6 +1147,7 @@ meta_compositor_sync_stack (MetaCompositor  *compositor,
1c7749
 			    GList	    *stack)
1c7749
 {
1c7749
   GList *old_stack;
1c7749
+  int stereo_window_count = 0;
1c7749
 
1c7749
   /* This is painful because hidden windows that we are in the process
1c7749
    * of animating out of existence. They'll be at the bottom of the
1c7749
@@ -1113,6 +1223,8 @@ meta_compositor_sync_stack (MetaCompositor  *compositor,
1c7749
        * near the front of the other.)
1c7749
        */
1c7749
       compositor->windows = g_list_prepend (compositor->windows, actor);
1c7749
+      if (meta_window_actor_is_stereo (actor))
1c7749
+        stereo_window_count++;
1c7749
 
1c7749
       stack = g_list_remove (stack, window);
1c7749
       old_stack = g_list_remove (old_stack, actor);
1c7749
@@ -1120,6 +1232,8 @@ meta_compositor_sync_stack (MetaCompositor  *compositor,
1c7749
 
1c7749
   sync_actor_stacking (compositor);
1c7749
 
1c7749
+  meta_stereo_set_have_stereo_windows (stereo_window_count > 0);
1c7749
+
1c7749
   if (compositor->top_window_actor)
1c7749
     g_signal_handlers_disconnect_by_func (compositor->top_window_actor,
1c7749
                                           on_top_window_actor_destroyed,
1c7749
@@ -1325,6 +1439,17 @@ meta_compositor_new (MetaDisplay *display)
1c7749
                                            meta_post_paint_func,
1c7749
                                            compositor,
1c7749
                                            NULL);
1c7749
+  if (!meta_is_wayland_compositor ())
1c7749
+    {
1c7749
+      Display *xdisplay = meta_x11_display_get_xdisplay (display->x11_display);
1c7749
+      int glx_major_opcode, glx_first_event, glx_first_error;
1c7749
+
1c7749
+      if (XQueryExtension (xdisplay,
1c7749
+                           "GLX",
1c7749
+                           &glx_major_opcode, &glx_first_event, &glx_first_error))
1c7749
+        compositor->glx_opcode = glx_major_opcode;
1c7749
+    }
1c7749
+
1c7749
   return compositor;
1c7749
 }
1c7749
 
1c7749
diff --git a/src/compositor/meta-shaped-texture-private.h b/src/compositor/meta-shaped-texture-private.h
1c7749
index a86a2bff0..d0efdd4dc 100644
1c7749
--- a/src/compositor/meta-shaped-texture-private.h
1c7749
+++ b/src/compositor/meta-shaped-texture-private.h
1c7749
@@ -31,8 +31,9 @@
1c7749
 #include "meta/meta-shaped-texture.h"
1c7749
 
1c7749
 ClutterActor *meta_shaped_texture_new (void);
1c7749
-void meta_shaped_texture_set_texture (MetaShapedTexture *stex,
1c7749
-                                      CoglTexture       *texture);
1c7749
+void meta_shaped_texture_set_textures (MetaShapedTexture *stex,
1c7749
+                                       CoglTexture       *texture,
1c7749
+                                       CoglTexture       *texture_right);
1c7749
 void meta_shaped_texture_set_is_y_inverted (MetaShapedTexture *stex,
1c7749
                                             gboolean           is_y_inverted);
1c7749
 void meta_shaped_texture_set_snippet (MetaShapedTexture *stex,
1c7749
diff --git a/src/compositor/meta-shaped-texture.c b/src/compositor/meta-shaped-texture.c
1c7749
index ea8daa03d..9665ce2b6 100644
1c7749
--- a/src/compositor/meta-shaped-texture.c
1c7749
+++ b/src/compositor/meta-shaped-texture.c
1c7749
@@ -102,8 +102,10 @@ struct _MetaShapedTexture
1c7749
   ClutterActor parent;
1c7749
 
1c7749
   MetaTextureTower *paint_tower;
1c7749
+  MetaTextureTower *paint_tower_right;
1c7749
 
1c7749
   CoglTexture *texture;
1c7749
+  CoglTexture *texture_right;
1c7749
   CoglTexture *mask_texture;
1c7749
   CoglSnippet *snippet;
1c7749
 
1c7749
@@ -193,6 +195,9 @@ meta_shaped_texture_init (MetaShapedTexture *stex)
1c7749
 
1c7749
   stex->paint_tower = meta_texture_tower_new ();
1c7749
 
1c7749
+  stex->paint_tower = meta_texture_tower_new ();
1c7749
+  stex->paint_tower_right = NULL; /* demand create */
1c7749
+
1c7749
   stex->texture = NULL;
1c7749
   stex->mask_texture = NULL;
1c7749
   stex->create_mipmaps = TRUE;
1c7749
@@ -335,6 +340,9 @@ meta_shaped_texture_dispose (GObject *object)
1c7749
     meta_texture_tower_free (stex->paint_tower);
1c7749
   stex->paint_tower = NULL;
1c7749
 
1c7749
+  g_clear_pointer (&stex->paint_tower, meta_texture_tower_free);
1c7749
+  g_clear_pointer (&stex->paint_tower_right, meta_texture_tower_free);
1c7749
+
1c7749
   g_clear_pointer (&stex->texture, cogl_object_unref);
1c7749
   g_clear_pointer (&stex->opaque_region, cairo_region_destroy);
1c7749
 
1c7749
@@ -611,8 +619,9 @@ paint_clipped_rectangle (MetaShapedTexture     *stex,
1c7749
 }
1c7749
 
1c7749
 static void
1c7749
-set_cogl_texture (MetaShapedTexture *stex,
1c7749
-                  CoglTexture       *cogl_tex)
1c7749
+set_cogl_textures (MetaShapedTexture *stex,
1c7749
+                   CoglTexture       *cogl_tex,
1c7749
+                   CoglTexture       *cogl_tex_right)
1c7749
 {
1c7749
   int width, height;
1c7749
 
1c7749
@@ -620,10 +629,13 @@ set_cogl_texture (MetaShapedTexture *stex,
1c7749
 
1c7749
   if (stex->texture)
1c7749
     cogl_object_unref (stex->texture);
1c7749
+  if (stex->texture_right)
1c7749
+    cogl_object_unref (stex->texture_right);
1c7749
 
1c7749
   g_clear_pointer (&stex->saved_base_surface, cairo_surface_destroy);
1c7749
 
1c7749
   stex->texture = cogl_tex;
1c7749
+  stex->texture_right = cogl_tex_right;
1c7749
 
1c7749
   if (cogl_tex != NULL)
1c7749
     {
1c7749
@@ -637,6 +649,9 @@ set_cogl_texture (MetaShapedTexture *stex,
1c7749
       height = 0;
1c7749
     }
1c7749
 
1c7749
+  if (cogl_tex_right != NULL)
1c7749
+    cogl_object_ref (cogl_tex_right);
1c7749
+
1c7749
   if (stex->tex_width != width ||
1c7749
       stex->tex_height != height)
1c7749
     {
1c7749
@@ -650,8 +665,23 @@ set_cogl_texture (MetaShapedTexture *stex,
1c7749
    * previous buffer. We only queue a redraw in response to surface
1c7749
    * damage. */
1c7749
 
1c7749
+  if (cogl_tex_right != NULL)
1c7749
+    {
1c7749
+      if (stex->paint_tower_right == NULL)
1c7749
+        stex->paint_tower_right = meta_texture_tower_new ();
1c7749
+    }
1c7749
+  else
1c7749
+    {
1c7749
+      g_clear_pointer (&stex->paint_tower_right, meta_texture_tower_free);
1c7749
+    }
1c7749
+
1c7749
   if (stex->create_mipmaps)
1c7749
-    meta_texture_tower_set_base_texture (stex->paint_tower, cogl_tex);
1c7749
+    {
1c7749
+      meta_texture_tower_set_base_texture (stex->paint_tower, cogl_tex);
1c7749
+
1c7749
+      if (stex->paint_tower_right)
1c7749
+        meta_texture_tower_set_base_texture (stex->paint_tower_right, cogl_tex_right);
1c7749
+    }
1c7749
 }
1c7749
 
1c7749
 static gboolean
1c7749
@@ -927,7 +957,9 @@ meta_shaped_texture_paint (ClutterActor *actor)
1c7749
 {
1c7749
   MetaShapedTexture *stex = META_SHAPED_TEXTURE (actor);
1c7749
   CoglTexture *paint_tex;
1c7749
+  CoglTexture *paint_tex_right = NULL;
1c7749
   CoglFramebuffer *fb;
1c7749
+  gboolean stereo;
1c7749
 
1c7749
   if (!stex->texture)
1c7749
     return;
1c7749
@@ -989,7 +1021,32 @@ meta_shaped_texture_paint (ClutterActor *actor)
1c7749
     return;
1c7749
 
1c7749
   fb = cogl_get_draw_framebuffer ();
1c7749
-  do_paint (META_SHAPED_TEXTURE (actor), fb, paint_tex, stex->clip_region);
1c7749
+
1c7749
+  stereo = stex->texture_right && cogl_framebuffer_get_is_stereo (fb);
1c7749
+
1c7749
+  if (stereo)
1c7749
+    {
1c7749
+      if (stex->create_mipmaps)
1c7749
+	paint_tex_right = meta_texture_tower_get_paint_texture (stex->paint_tower_right);
1c7749
+
1c7749
+      if (!paint_tex_right)
1c7749
+	paint_tex_right = COGL_TEXTURE (stex->texture_right);
1c7749
+    }
1c7749
+  else
1c7749
+    paint_tex_right = NULL;
1c7749
+
1c7749
+  if (stereo)
1c7749
+    cogl_framebuffer_set_stereo_mode (fb, COGL_STEREO_LEFT);
1c7749
+  do_paint (stex, fb, paint_tex, stex->clip_region);
1c7749
+  if (stereo)
1c7749
+    cogl_framebuffer_set_stereo_mode (fb, COGL_STEREO_BOTH);
1c7749
+
1c7749
+  if (paint_tex_right != NULL)
1c7749
+    {
1c7749
+      cogl_framebuffer_set_stereo_mode (fb, COGL_STEREO_RIGHT);
1c7749
+      do_paint (stex, fb, paint_tex_right, stex->clip_region);
1c7749
+      cogl_framebuffer_set_stereo_mode (fb, COGL_STEREO_BOTH);
1c7749
+    }
1c7749
 }
1c7749
 
1c7749
 static void
1c7749
@@ -1063,6 +1120,12 @@ meta_shaped_texture_set_create_mipmaps (MetaShapedTexture *stex,
1c7749
       stex->create_mipmaps = create_mipmaps;
1c7749
       base_texture = create_mipmaps ? stex->texture : NULL;
1c7749
       meta_texture_tower_set_base_texture (stex->paint_tower, base_texture);
1c7749
+
1c7749
+      if (stex->paint_tower_right)
1c7749
+        {
1c7749
+          base_texture = create_mipmaps ? stex->texture_right : NULL;
1c7749
+          meta_texture_tower_set_base_texture (stex->paint_tower_right, base_texture);
1c7749
+        }
1c7749
     }
1c7749
 }
1c7749
 
1c7749
@@ -1256,6 +1319,12 @@ meta_shaped_texture_update_area (MetaShapedTexture *stex,
1c7749
                                   clip.y,
1c7749
                                   clip.width,
1c7749
                                   clip.height);
1c7749
+  if (stex->paint_tower_right)
1c7749
+    meta_texture_tower_update_area (stex->paint_tower_right,
1c7749
+                                    clip.x,
1c7749
+                                    clip.y,
1c7749
+                                    clip.width,
1c7749
+                                    clip.height);
1c7749
 
1c7749
   stex->prev_invalidation = stex->last_invalidation;
1c7749
   stex->last_invalidation = g_get_monotonic_time ();
1c7749
@@ -1302,17 +1371,18 @@ meta_shaped_texture_update_area (MetaShapedTexture *stex,
1c7749
 }
1c7749
 
1c7749
 /**
1c7749
- * meta_shaped_texture_set_texture:
1c7749
+ * meta_shaped_texture_set_textures:
1c7749
  * @stex: The #MetaShapedTexture
1c7749
  * @pixmap: The #CoglTexture to display
1c7749
  */
1c7749
 void
1c7749
-meta_shaped_texture_set_texture (MetaShapedTexture *stex,
1c7749
-                                 CoglTexture       *texture)
1c7749
+meta_shaped_texture_set_textures (MetaShapedTexture *stex,
1c7749
+                                  CoglTexture       *texture,
1c7749
+                                  CoglTexture       *texture_right)
1c7749
 {
1c7749
   g_return_if_fail (META_IS_SHAPED_TEXTURE (stex));
1c7749
 
1c7749
-  set_cogl_texture (stex, texture);
1c7749
+  set_cogl_textures (stex, texture, texture_right);
1c7749
 }
1c7749
 
1c7749
 /**
1c7749
diff --git a/src/compositor/meta-surface-actor-wayland.c b/src/compositor/meta-surface-actor-wayland.c
1c7749
index f8d6c32b7..a75c4dd09 100644
1c7749
--- a/src/compositor/meta-surface-actor-wayland.c
1c7749
+++ b/src/compositor/meta-surface-actor-wayland.c
1c7749
@@ -182,7 +182,7 @@ meta_surface_actor_wayland_dispose (GObject *object)
1c7749
   MetaShapedTexture *stex =
1c7749
     meta_surface_actor_get_texture (META_SURFACE_ACTOR (self));
1c7749
 
1c7749
-  meta_shaped_texture_set_texture (stex, NULL);
1c7749
+  meta_shaped_texture_set_textures (stex, NULL, NULL);
1c7749
   if (self->surface)
1c7749
     {
1c7749
       g_object_remove_weak_pointer (G_OBJECT (self->surface),
1c7749
diff --git a/src/compositor/meta-surface-actor-x11.c b/src/compositor/meta-surface-actor-x11.c
1c7749
index 244b1e885..3cd164d77 100644
1c7749
--- a/src/compositor/meta-surface-actor-x11.c
1c7749
+++ b/src/compositor/meta-surface-actor-x11.c
1c7749
@@ -32,6 +32,7 @@
1c7749
 #include "cogl/winsys/cogl-texture-pixmap-x11.h"
1c7749
 #include "compositor/meta-cullable.h"
1c7749
 #include "compositor/meta-shaped-texture-private.h"
1c7749
+#include "compositor-private.h"
1c7749
 #include "core/window-private.h"
1c7749
 #include "meta/meta-x11-errors.h"
1c7749
 #include "x11/meta-x11-display-private.h"
1c7749
@@ -46,6 +47,7 @@ struct _MetaSurfaceActorX11
1c7749
   MetaDisplay *display;
1c7749
 
1c7749
   CoglTexture *texture;
1c7749
+  CoglTexture *texture_right;
1c7749
   Pixmap pixmap;
1c7749
   Damage damage;
1c7749
 
1c7749
@@ -61,6 +63,8 @@ struct _MetaSurfaceActorX11
1c7749
   guint size_changed : 1;
1c7749
 
1c7749
   guint unredirected   : 1;
1c7749
+
1c7749
+  guint stereo : 1;
1c7749
 };
1c7749
 
1c7749
 G_DEFINE_TYPE (MetaSurfaceActorX11,
1c7749
@@ -96,7 +100,7 @@ detach_pixmap (MetaSurfaceActorX11 *self)
1c7749
    * you are supposed to be able to free a GLXPixmap after freeing the underlying
1c7749
    * pixmap, but it certainly doesn't work with current DRI/Mesa
1c7749
    */
1c7749
-  meta_shaped_texture_set_texture (stex, NULL);
1c7749
+  meta_shaped_texture_set_textures (stex, NULL, NULL);
1c7749
   cogl_flush ();
1c7749
 
1c7749
   meta_x11_error_trap_push (display->x11_display);
1c7749
@@ -105,6 +109,7 @@ detach_pixmap (MetaSurfaceActorX11 *self)
1c7749
   meta_x11_error_trap_pop (display->x11_display);
1c7749
 
1c7749
   g_clear_pointer (&self->texture, cogl_object_unref);
1c7749
+  g_clear_pointer (&self->texture_right, cogl_object_unref);
1c7749
 }
1c7749
 
1c7749
 static void
1c7749
@@ -114,23 +119,37 @@ set_pixmap (MetaSurfaceActorX11 *self,
1c7749
   CoglContext *ctx = clutter_backend_get_cogl_context (clutter_get_default_backend ());
1c7749
   MetaShapedTexture *stex = meta_surface_actor_get_texture (META_SURFACE_ACTOR (self));
1c7749
   CoglError *error = NULL;
1c7749
-  CoglTexture *texture;
1c7749
+  CoglTexturePixmapX11 *texture;
1c7749
+  CoglTexturePixmapX11 *texture_right;
1c7749
 
1c7749
   g_assert (self->pixmap == None);
1c7749
   self->pixmap = pixmap;
1c7749
 
1c7749
-  texture = COGL_TEXTURE (cogl_texture_pixmap_x11_new (ctx, self->pixmap, FALSE, &error));
1c7749
+  if (self->stereo)
1c7749
+    texture = cogl_texture_pixmap_x11_new_left (ctx, pixmap, FALSE, &error);
1c7749
+  else
1c7749
+    texture = cogl_texture_pixmap_x11_new (ctx, pixmap, FALSE, &error);
1c7749
+
1c7749
+  if (self->stereo)
1c7749
+    texture_right = cogl_texture_pixmap_x11_new_right (texture);
1c7749
+  else
1c7749
+    texture_right = NULL;
1c7749
 
1c7749
   if (error != NULL)
1c7749
     {
1c7749
       g_warning ("Failed to allocate stex texture: %s", error->message);
1c7749
       cogl_error_free (error);
1c7749
     }
1c7749
-  else if (G_UNLIKELY (!cogl_texture_pixmap_x11_is_using_tfp_extension (COGL_TEXTURE_PIXMAP_X11 (texture))))
1c7749
+  else if (G_UNLIKELY (!cogl_texture_pixmap_x11_is_using_tfp_extension (texture)))
1c7749
     g_warning ("NOTE: Not using GLX TFP!\n");
1c7749
 
1c7749
-  self->texture = texture;
1c7749
-  meta_shaped_texture_set_texture (stex, texture);
1c7749
+  self->texture = COGL_TEXTURE (texture);
1c7749
+  if (self->stereo)
1c7749
+    self->texture_right = COGL_TEXTURE (texture_right);
1c7749
+
1c7749
+  meta_shaped_texture_set_textures (stex,
1c7749
+                                    COGL_TEXTURE (texture),
1c7749
+                                    COGL_TEXTURE (texture_right));;
1c7749
 }
1c7749
 
1c7749
 static void
1c7749
@@ -419,8 +438,8 @@ reset_texture (MetaSurfaceActorX11 *self)
1c7749
   /* Setting the texture to NULL will cause all the FBO's cached by the
1c7749
    * shaped texture's MetaTextureTower to be discarded and recreated.
1c7749
    */
1c7749
-  meta_shaped_texture_set_texture (stex, NULL);
1c7749
-  meta_shaped_texture_set_texture (stex, self->texture);
1c7749
+  meta_shaped_texture_set_textures (stex, NULL, NULL);
1c7749
+  meta_shaped_texture_set_textures (stex, self->texture, self->texture_right);
1c7749
 }
1c7749
 
1c7749
 MetaSurfaceActor *
1c7749
@@ -428,12 +447,17 @@ meta_surface_actor_x11_new (MetaWindow *window)
1c7749
 {
1c7749
   MetaSurfaceActorX11 *self = g_object_new (META_TYPE_SURFACE_ACTOR_X11, NULL);
1c7749
   MetaDisplay *display = meta_window_get_display (window);
1c7749
+  Window xwindow;
1c7749
 
1c7749
   g_assert (!meta_is_wayland_compositor ());
1c7749
 
1c7749
   self->window = window;
1c7749
   self->display = display;
1c7749
 
1c7749
+  xwindow = meta_window_x11_get_toplevel_xwindow (window);
1c7749
+  self->stereo = meta_compositor_window_is_stereo (display, xwindow);
1c7749
+  meta_compositor_select_stereo_notify (display, xwindow);
1c7749
+
1c7749
   g_signal_connect_object (self->display, "gl-video-memory-purged",
1c7749
                            G_CALLBACK (reset_texture), self, G_CONNECT_SWAPPED);
1c7749
 
1c7749
@@ -463,3 +487,17 @@ meta_surface_actor_x11_set_size (MetaSurfaceActorX11 *self,
1c7749
   self->last_height = height;
1c7749
   meta_shaped_texture_set_fallback_size (stex, width, height);
1c7749
 }
1c7749
+
1c7749
+void
1c7749
+meta_surface_actor_x11_stereo_notify (MetaSurfaceActorX11 *self,
1c7749
+                                      gboolean             stereo_tree)
1c7749
+{
1c7749
+  self->stereo = stereo_tree != FALSE;
1c7749
+  detach_pixmap (self);
1c7749
+}
1c7749
+
1c7749
+gboolean
1c7749
+meta_surface_actor_x11_is_stereo (MetaSurfaceActorX11 *self)
1c7749
+{
1c7749
+  return self->stereo;
1c7749
+}
1c7749
diff --git a/src/compositor/meta-surface-actor-x11.h b/src/compositor/meta-surface-actor-x11.h
1c7749
index 2c4ed4dd6..3bdd5fdb0 100644
1c7749
--- a/src/compositor/meta-surface-actor-x11.h
1c7749
+++ b/src/compositor/meta-surface-actor-x11.h
1c7749
@@ -47,6 +47,11 @@ MetaSurfaceActor * meta_surface_actor_x11_new (MetaWindow *window);
1c7749
 void meta_surface_actor_x11_set_size (MetaSurfaceActorX11 *self,
1c7749
                                       int width, int height);
1c7749
 
1c7749
+void meta_surface_actor_x11_stereo_notify (MetaSurfaceActorX11 *self,
1c7749
+                                           gboolean             stereo_tree);
1c7749
+
1c7749
+gboolean meta_surface_actor_x11_is_stereo (MetaSurfaceActorX11 *self);
1c7749
+
1c7749
 G_END_DECLS
1c7749
 
1c7749
 #endif /* __META_SURFACE_ACTOR_X11_H__ */
1c7749
diff --git a/src/compositor/meta-window-actor-private.h b/src/compositor/meta-window-actor-private.h
1c7749
index 6333f43db..9c1c12d09 100644
1c7749
--- a/src/compositor/meta-window-actor-private.h
1c7749
+++ b/src/compositor/meta-window-actor-private.h
1c7749
@@ -76,4 +76,9 @@ MetaSurfaceActor *meta_window_actor_get_surface (MetaWindowActor *self);
1c7749
 void meta_window_actor_update_surface (MetaWindowActor *self);
1c7749
 MetaWindowActor *meta_window_actor_from_window (MetaWindow *window);
1c7749
 
1c7749
+void meta_window_actor_stereo_notify (MetaWindowActor *actor,
1c7749
+                                      gboolean         stereo_tree);
1c7749
+
1c7749
+gboolean meta_window_actor_is_stereo (MetaWindowActor *actor);
1c7749
+
1c7749
 #endif /* META_WINDOW_ACTOR_PRIVATE_H */
1c7749
diff --git a/src/compositor/meta-window-actor.c b/src/compositor/meta-window-actor.c
1c7749
index 1c8dc8fe5..11686d00b 100644
1c7749
--- a/src/compositor/meta-window-actor.c
1c7749
+++ b/src/compositor/meta-window-actor.c
1c7749
@@ -2031,3 +2031,25 @@ screen_cast_window_iface_init (MetaScreenCastWindowInterface *iface)
1c7749
   iface->capture_into = meta_window_actor_capture_into;
1c7749
   iface->has_damage = meta_window_actor_has_damage;
1c7749
 }
1c7749
+
1c7749
+void
1c7749
+meta_window_actor_stereo_notify (MetaWindowActor *self,
1c7749
+                                 gboolean         stereo_tree)
1c7749
+{
1c7749
+  MetaWindowActorPrivate *priv = meta_window_actor_get_instance_private (self);
1c7749
+
1c7749
+  if (META_IS_SURFACE_ACTOR_X11 (priv->surface))
1c7749
+    meta_surface_actor_x11_stereo_notify (META_SURFACE_ACTOR_X11 (priv->surface),
1c7749
+                                          stereo_tree);
1c7749
+}
1c7749
+
1c7749
+gboolean
1c7749
+meta_window_actor_is_stereo (MetaWindowActor *self)
1c7749
+{
1c7749
+  MetaWindowActorPrivate *priv = meta_window_actor_get_instance_private (self);
1c7749
+
1c7749
+  if (META_IS_SURFACE_ACTOR_X11 (priv->surface))
1c7749
+    return meta_surface_actor_x11_is_stereo (META_SURFACE_ACTOR_X11 (priv->surface));
1c7749
+  else
1c7749
+    return FALSE;
1c7749
+}
1c7749
diff --git a/src/core/main.c b/src/core/main.c
1c7749
index e8464720f..629f8e94e 100644
1c7749
--- a/src/core/main.c
1c7749
+++ b/src/core/main.c
1c7749
@@ -81,6 +81,7 @@
1c7749
 #include "meta/meta-backend.h"
1c7749
 #include "meta/meta-x11-errors.h"
1c7749
 #include "meta/prefs.h"
1c7749
+#include "stereo.h"
1c7749
 #include "ui/ui.h"
1c7749
 #include "x11/session.h"
1c7749
 
1c7749
@@ -589,6 +590,9 @@ meta_init (void)
1c7749
 
1c7749
   meta_init_backend (backend_gtype);
1c7749
 
1c7749
+  if (!meta_is_wayland_compositor ())
1c7749
+    meta_stereo_init ();
1c7749
+
1c7749
   meta_clutter_init ();
1c7749
 
1c7749
 #ifdef HAVE_WAYLAND
1c7749
diff --git a/src/core/stereo.c b/src/core/stereo.c
1c7749
new file mode 100644
1c7749
index 000000000..817056527
1c7749
--- /dev/null
1c7749
+++ b/src/core/stereo.c
1c7749
@@ -0,0 +1,154 @@
1c7749
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
1c7749
+
1c7749
+/*
1c7749
+ * Copyright (C) 2014 Red Hat, Inc.
1c7749
+ *
1c7749
+ * This program is free software; you can redistribute it and/or
1c7749
+ * modify it under the terms of the GNU General Public License as
1c7749
+ * published by the Free Software Foundation; either version 2 of the
1c7749
+ * License, or (at your option) any later version.
1c7749
+ *
1c7749
+ * This program is distributed in the hope that it will be useful, but
1c7749
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
1c7749
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
1c7749
+ * General Public License for more details.
1c7749
+ *
1c7749
+ * You should have received a copy of the GNU General Public License
1c7749
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
1c7749
+ */
1c7749
+
1c7749
+/*
1c7749
+ * SECTION:stereo
1c7749
+ * @short_description: Keep track of whether we are a stereo compositor
1c7749
+ *
1c7749
+ * With GLX, we need to use a different GL context for stereo and
1c7749
+ * non-stereo support. Support for multiple GL contexts is unfinished
1c7749
+ * in Cogl and entirely lacking in Clutter, so it's by far easier
1c7749
+ * to just restart Mutter when we detect a stereo window.
1c7749
+ *
1c7749
+ * A property _MUTTER_ENABLE_STEREO is maintained on the root window
1c7749
+ * to know whether we should initialize clutter for stereo or not.
1c7749
+ * When the presence or absence of stereo windows mismatches the
1c7749
+ * stereo-enabled state for a sufficiently long period of time,
1c7749
+ * we restart Mutter.
1c7749
+ */
1c7749
+
1c7749
+#include <config.h>
1c7749
+
1c7749
+#include <clutter/x11/clutter-x11.h>
1c7749
+#include <gio/gunixinputstream.h>
1c7749
+#include <X11/Xatom.h>
1c7749
+
1c7749
+#include "display-private.h"
1c7749
+#include <meta/main.h>
1c7749
+#include <meta/meta-x11-display.h>
1c7749
+#include <meta/util.h>
1c7749
+#include "stereo.h"
1c7749
+#include "ui/ui.h"
1c7749
+#include "util-private.h"
1c7749
+
1c7749
+static guint stereo_switch_id = 0;
1c7749
+static gboolean stereo_enabled = FALSE;
1c7749
+/* -1 so the first time meta_stereo_set_have_stereo_windows() is called
1c7749
+ * we avoid the short-circuit and set up a timeout to restart
1c7749
+ * if necessary */
1c7749
+static gboolean stereo_have_windows = (gboolean)-1;
1c7749
+static gboolean stereo_restart = FALSE;
1c7749
+
1c7749
+#define STEREO_ENABLE_WAIT 1000
1c7749
+#define STEREO_DISABLE_WAIT 5000
1c7749
+
1c7749
+void
1c7749
+meta_stereo_init (void)
1c7749
+{
1c7749
+  Display *xdisplay;
1c7749
+  Window root;
1c7749
+  Atom atom_enable_stereo;
1c7749
+  Atom type;
1c7749
+  int format;
1c7749
+  unsigned long n_items, bytes_after;
1c7749
+  guchar *data;
1c7749
+
1c7749
+  xdisplay = XOpenDisplay (NULL);
1c7749
+  if (xdisplay == NULL)
1c7749
+    meta_fatal ("Unable to open X display %s\n", XDisplayName (NULL));
1c7749
+
1c7749
+  root = DefaultRootWindow (xdisplay);
1c7749
+  atom_enable_stereo = XInternAtom (xdisplay, "_MUTTER_ENABLE_STEREO", False);
1c7749
+
1c7749
+  XGetWindowProperty (xdisplay, root, atom_enable_stereo,
1c7749
+                      0, 1, False, XA_INTEGER,
1c7749
+                      &type, &format, &n_items, &bytes_after, &data);
1c7749
+  if (type == XA_INTEGER)
1c7749
+    {
1c7749
+      if (format == 32 && n_items == 1 && bytes_after == 0)
1c7749
+        {
1c7749
+          stereo_enabled = *(long *)data;
1c7749
+        }
1c7749
+      else
1c7749
+        {
1c7749
+          meta_warning ("Bad value for _MUTTER_ENABLE_STEREO property\n");
1c7749
+        }
1c7749
+
1c7749
+      XFree (data);
1c7749
+    }
1c7749
+  else if (type != None)
1c7749
+    {
1c7749
+      meta_warning ("Bad type for _MUTTER_ENABLE_STEREO property\n");
1c7749
+    }
1c7749
+
1c7749
+  meta_verbose ("On startup, _MUTTER_ENABLE_STEREO=%s",
1c7749
+                stereo_enabled ? "yes" : "no");
1c7749
+  clutter_x11_set_use_stereo_stage (stereo_enabled);
1c7749
+  XCloseDisplay (xdisplay);
1c7749
+}
1c7749
+
1c7749
+static gboolean
1c7749
+meta_stereo_switch (gpointer data)
1c7749
+{
1c7749
+  stereo_switch_id = 0;
1c7749
+  stereo_restart = TRUE;
1c7749
+
1c7749
+  meta_restart (stereo_have_windows ?
1c7749
+                _("Enabling stereo...") :
1c7749
+                _("Disabling stereo..."));
1c7749
+
1c7749
+  return FALSE;
1c7749
+}
1c7749
+
1c7749
+void
1c7749
+meta_stereo_set_have_stereo_windows (gboolean have_windows)
1c7749
+{
1c7749
+  have_windows = have_windows != FALSE;
1c7749
+
1c7749
+  if (!stereo_restart && have_windows != stereo_have_windows)
1c7749
+    {
1c7749
+      MetaDisplay *display = meta_get_display ();
1c7749
+      Display *xdisplay = meta_x11_display_get_xdisplay (display->x11_display);
1c7749
+      Window root = DefaultRootWindow (xdisplay);
1c7749
+      Atom atom_enable_stereo = XInternAtom (xdisplay, "_MUTTER_ENABLE_STEREO", False);
1c7749
+      long value;
1c7749
+
1c7749
+      stereo_have_windows = have_windows;
1c7749
+
1c7749
+      if (stereo_have_windows)
1c7749
+        meta_verbose ("Detected stereo windows\n");
1c7749
+      else
1c7749
+        meta_verbose ("No stereo windows detected\n");
1c7749
+
1c7749
+      value = stereo_have_windows;
1c7749
+      XChangeProperty (xdisplay, root,
1c7749
+                       atom_enable_stereo, XA_INTEGER, 32,
1c7749
+                       PropModeReplace, (guchar *)&value, 1);
1c7749
+
1c7749
+      if (stereo_switch_id != 0)
1c7749
+        {
1c7749
+          g_source_remove (stereo_switch_id);
1c7749
+          stereo_switch_id = 0;
1c7749
+        }
1c7749
+
1c7749
+      if (stereo_have_windows != stereo_enabled)
1c7749
+        stereo_switch_id = g_timeout_add (stereo_have_windows ? STEREO_ENABLE_WAIT : STEREO_DISABLE_WAIT,
1c7749
+                                          meta_stereo_switch, NULL);
1c7749
+    }
1c7749
+}
1c7749
diff --git a/src/core/stereo.h b/src/core/stereo.h
1c7749
new file mode 100644
1c7749
index 000000000..ccd1d702a
1c7749
--- /dev/null
1c7749
+++ b/src/core/stereo.h
1c7749
@@ -0,0 +1,28 @@
1c7749
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
1c7749
+
1c7749
+/*
1c7749
+ * Copyright (C) 2014 Red Hat, Inc.
1c7749
+ *
1c7749
+ * This program is free software; you can redistribute it and/or
1c7749
+ * modify it under the terms of the GNU General Public License as
1c7749
+ * published by the Free Software Foundation; either version 2 of the
1c7749
+ * License, or (at your option) any later version.
1c7749
+ *
1c7749
+ * This program is distributed in the hope that it will be useful, but
1c7749
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
1c7749
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
1c7749
+ * General Public License for more details.
1c7749
+ *
1c7749
+ * You should have received a copy of the GNU General Public License
1c7749
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
1c7749
+ */
1c7749
+
1c7749
+#ifndef META_STEREO_H
1c7749
+#define META_STEREO_H
1c7749
+
1c7749
+void     meta_stereo_init                    (void);
1c7749
+void     meta_stereo_set_have_stereo_windows (gboolean have_windows);
1c7749
+gboolean meta_stereo_is_restart              (void);
1c7749
+void     meta_stereo_finish_restart          (void);
1c7749
+
1c7749
+#endif
1c7749
diff --git a/src/meson.build b/src/meson.build
1c7749
index 9919b5cfb..7cced8f53 100644
1c7749
--- a/src/meson.build
1c7749
+++ b/src/meson.build
1c7749
@@ -353,6 +353,8 @@ mutter_sources = [
1c7749
   'core/stack.h',
1c7749
   'core/stack-tracker.c',
1c7749
   'core/stack-tracker.h',
1c7749
+  'core/stereo.c',
1c7749
+  'core/stereo.h',
1c7749
   'core/startup-notification.c',
1c7749
   'core/startup-notification-private.h',
1c7749
   'core/util.c',
1c7749
diff --git a/src/wayland/meta-wayland-surface.c b/src/wayland/meta-wayland-surface.c
1c7749
index da0acfcbb..ddad1a45c 100644
1c7749
--- a/src/wayland/meta-wayland-surface.c
1c7749
+++ b/src/wayland/meta-wayland-surface.c
1c7749
@@ -731,7 +731,7 @@ meta_wayland_surface_apply_pending_state (MetaWaylandSurface      *surface,
1c7749
               snippet = meta_wayland_buffer_create_snippet (pending->buffer);
1c7749
               is_y_inverted = meta_wayland_buffer_is_y_inverted (pending->buffer);
1c7749
 
1c7749
-              meta_shaped_texture_set_texture (stex, texture);
1c7749
+              meta_shaped_texture_set_textures (stex, texture, NULL);
1c7749
               meta_shaped_texture_set_snippet (stex, snippet);
1c7749
               meta_shaped_texture_set_is_y_inverted (stex, is_y_inverted);
1c7749
               g_clear_pointer (&snippet, cogl_object_unref);
1c7749
-- 
1c7749
2.21.0
1c7749