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

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