[mutter/wip/quadbuffer-stereo: 2/2] Add support for quad-buffer stereo



commit eef973d6ac305af82223db2d1b5832249f27b09d
Author: Owen W. Taylor <otaylor fishsoup net>
Date:   Thu May 8 18:44:15 2014 -0400

    Add support for quad-buffer stereo
    
    Track the stereo status of windows using the new EXT_stereo_tree
    GLX extension.
    
    When stereo is enabled or disabled, a restart is triggered via
    meta_restart() after a timeout, setting a _META_ENABLE_STEREO
    property on the root window to indicate whether we should
    turn on a stereo stage for clutter. The property avoids a loop,
    since we need to enable stereo *before* initializing Clutter and GL,
    but we need GL to figure out whether we have stereo windows.
    
    Stereo windows are drawn to the stage using new functionality
    in Cogl to setup a stereo context, select which buffer to draw
    to, and draw either the left or right buffer of a stereo
    texture_from_pixmap.

 src/Makefile.am                              |    2 +
 src/compositor/compositor-private.h          |   10 ++
 src/compositor/compositor.c                  |  123 ++++++++++++++++++++
 src/compositor/meta-shaped-texture-private.h |    3 +-
 src/compositor/meta-shaped-texture.c         |  155 +++++++++++++++++++-------
 src/compositor/meta-window-actor-private.h   |    7 +
 src/compositor/meta-window-actor.c           |   34 +++++-
 src/core/stereo.c                            |  144 ++++++++++++++++++++++++
 src/core/stereo.h                            |   28 +++++
 9 files changed, 459 insertions(+), 47 deletions(-)
---
diff --git a/src/Makefile.am b/src/Makefile.am
index cbdb026..fc79ec2 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -135,6 +135,8 @@ libmutter_la_SOURCES =                              \
        core/restart.c                          \
        core/session.c                          \
        core/session.h                          \
+       core/stereo.c                           \
+       core/stereo.h                           \
        core/stack.c                            \
        core/stack.h                            \
        core/stack-tracker.c                    \
diff --git a/src/compositor/compositor-private.h b/src/compositor/compositor-private.h
index 18bcff6..f7359d7 100644
--- a/src/compositor/compositor-private.h
+++ b/src/compositor/compositor-private.h
@@ -26,6 +26,8 @@ struct _MetaCompositor
   gint64          server_time_query_time;
   gint64          server_time_offset;
 
+  int             glx_opcode;
+
   guint           server_time_is_monotonic_time : 1;
   guint           show_redraw : 1;
   guint           debug       : 1;
@@ -51,6 +53,9 @@ struct _MetaCompScreen
 
   gint                   switch_workspace_in_progress;
 
+  guint                  stereo_tree_ext : 1;
+  guint                  have_stereo_windows : 1;
+
   MetaPluginManager *plugin_mgr;
 };
 
@@ -72,4 +77,9 @@ gint64 meta_compositor_monotonic_time_to_server_time (MetaDisplay *display,
 
 void meta_check_end_modal (MetaScreen *screen);
 
+gboolean meta_compositor_window_is_stereo     (MetaScreen *screen,
+                                               Window      xwindow);
+void     meta_compositor_select_stereo_notify (MetaScreen *screen,
+                                               Window      xwindow);
+
 #endif /* META_COMPOSITOR_PRIVATE_H */
diff --git a/src/compositor/compositor.c b/src/compositor/compositor.c
index 1d22af6..44f8244 100644
--- a/src/compositor/compositor.c
+++ b/src/compositor/compositor.c
@@ -544,6 +544,101 @@ redirect_windows (MetaCompositor *compositor,
     }
 }
 
+#define GLX_STEREO_TREE_EXT        0x20F5
+#define GLX_STEREO_NOTIFY_MASK_EXT 0x00000001
+#define GLX_STEREO_NOTIFY_EXT      0x00000000
+
+typedef struct {
+  int type;
+  unsigned long serial;
+  Bool send_event;
+  Display *display;
+  int extension;
+  int evtype;
+  Drawable window;
+  Bool stereo_tree;
+} StereoNotifyEvent;
+
+static gboolean
+screen_has_stereo_tree_ext (MetaScreen *screen)
+{
+#if 0
+  MetaDisplay *display = meta_screen_get_display (screen);
+  Display     *xdisplay = meta_display_get_xdisplay (display);
+  const char *extensions_string;
+
+  static const char * (*query_extensions_string) (Display *display,
+                                                  int      screen);
+
+  if (query_extensions_string == NULL)
+    query_extensions_string =
+      (const char * (*) (Display *, int))
+      cogl_get_proc_address ("glXQueryExtensionsString");
+
+  extensions_string = query_extensions_string (xdisplay,
+                                               meta_screen_get_screen_number (screen));
+
+  return strstr (extensions_string, "EXT_stereo_tree") != 0;
+#else
+  return TRUE;
+#endif
+}
+
+#include <GL/gl.h>
+
+gboolean
+meta_compositor_window_is_stereo (MetaScreen *screen,
+                                  Window      xwindow)
+{
+  MetaCompScreen *info = meta_screen_get_compositor_data (screen);
+  MetaDisplay    *display = meta_screen_get_display (screen);
+  Display        *xdisplay = meta_display_get_xdisplay (display);
+
+  static int (*query_drawable) (Display      *dpy,
+                                Drawable      draw,
+                                int           attribute,
+                                unsigned int *value);
+
+  if (info->stereo_tree_ext)
+    {
+      unsigned int stereo_tree = 0;
+
+      if (query_drawable == NULL)
+        query_drawable =
+          (int (*) (Display *, Drawable, int, unsigned int *))
+          cogl_get_proc_address ("glXQueryDrawable");
+
+      query_drawable (xdisplay, xwindow, GLX_STEREO_TREE_EXT, &stereo_tree);
+
+      return stereo_tree != 0;
+    }
+  else
+    return FALSE;
+}
+
+void
+meta_compositor_select_stereo_notify (MetaScreen *screen,
+                                      Window      xwindow)
+{
+  MetaCompScreen *info = meta_screen_get_compositor_data (screen);
+  MetaDisplay    *display = meta_screen_get_display (screen);
+  Display        *xdisplay = meta_display_get_xdisplay (display);
+
+  static void (*select_event) (Display      *dpy,
+                               Drawable      draw,
+                               unsigned long event_mask);
+
+  if (info->stereo_tree_ext)
+    {
+      if (select_event == NULL)
+        select_event =
+          (void (*) (Display *, Drawable, unsigned long))
+          cogl_get_proc_address ("glXSelectEvent");
+
+      select_event (xdisplay, xwindow, GLX_STEREO_NOTIFY_MASK_EXT);
+    }
+}
+
 void
 meta_compositor_manage_screen (MetaCompositor *compositor,
                                MetaScreen     *screen)
@@ -566,6 +661,8 @@ meta_compositor_manage_screen (MetaCompositor *compositor,
   info->output = None;
   info->windows = NULL;
 
+  info->stereo_tree_ext = screen_has_stereo_tree_ext (screen);
+
   meta_screen_set_cm_selection (screen);
 
   info->stage = clutter_stage_new ();
@@ -956,6 +1053,22 @@ meta_compositor_process_event (MetaCompositor *compositor,
        }
     }
 
+  if (event->type == GenericEvent &&
+      event->xcookie.extension == compositor->glx_opcode)
+    {
+      if (event->xcookie.evtype == GLX_STEREO_NOTIFY_EXT)
+        {
+          StereoNotifyEvent *stereo_event = (StereoNotifyEvent *)(event->xcookie.data);
+          window = meta_display_lookup_x_window (compositor->display, stereo_event->window);
+
+          if (window != NULL)
+            {
+              MetaWindowActor *window_actor = META_WINDOW_ACTOR (meta_window_get_compositor_private 
(window));
+              meta_window_actor_stereo_notify (window_actor, stereo_event->stereo_tree);
+            }
+        }
+    }
+
   if (event->type == meta_display_get_damage_event_base (compositor->display) + XDamageNotify)
     {
       /* Core code doesn't handle damage events, so we need to extract the MetaWindow
@@ -1183,6 +1296,7 @@ meta_compositor_sync_stack (MetaCompositor  *compositor,
 {
   GList *old_stack;
   MetaCompScreen *info = meta_screen_get_compositor_data (screen);
+  int stereo_window_count = 0;
 
   DEBUG_TRACE ("meta_compositor_sync_stack\n");
 
@@ -1260,12 +1374,16 @@ meta_compositor_sync_stack (MetaCompositor  *compositor,
        * near the front of the other.)
        */
       info->windows = g_list_prepend (info->windows, actor);
+      if (meta_window_actor_is_stereo (actor))
+        stereo_window_count++;
 
       stack = g_list_remove (stack, window);
       old_stack = g_list_remove (old_stack, actor);
     }
 
   sync_actor_stacking (info);
+
+  meta_stereo_set_have_stereo_windows (stereo_window_count > 0);
 }
 
 void
@@ -1431,6 +1549,7 @@ MetaCompositor *
 meta_compositor_new (MetaDisplay *display)
 {
   MetaCompositor        *compositor;
+  int glx_major_opcode, glx_first_event, glx_first_error;
 
   if (!composite_at_least_version (display, 0, 3))
     return NULL;
@@ -1450,6 +1569,10 @@ meta_compositor_new (MetaDisplay *display)
   compositor->repaint_func_id = clutter_threads_add_repaint_func (meta_repaint_func,
                                                                   compositor,
                                                                   NULL);
+  if (XQueryExtension (meta_display_get_xdisplay (display),
+                       "GLX",
+                       &glx_major_opcode, &glx_first_event, &glx_first_error))
+    compositor->glx_opcode = glx_major_opcode;
 
   return compositor;
 }
diff --git a/src/compositor/meta-shaped-texture-private.h b/src/compositor/meta-shaped-texture-private.h
index 48ae0df..9a61438 100644
--- a/src/compositor/meta-shaped-texture-private.h
+++ b/src/compositor/meta-shaped-texture-private.h
@@ -31,7 +31,8 @@
 
 ClutterActor *meta_shaped_texture_new (void);
 void meta_shaped_texture_set_texture (MetaShapedTexture *stex,
-                                      CoglTexture       *texture);
+                                      CoglTexture       *texture,
+                                     gboolean           stereo);
 gboolean meta_shaped_texture_get_unobscured_bounds (MetaShapedTexture     *stex,
                                                     cairo_rectangle_int_t *unobscured_bounds);
 gboolean meta_shaped_texture_is_obscured (MetaShapedTexture *self);
diff --git a/src/compositor/meta-shaped-texture.c b/src/compositor/meta-shaped-texture.c
index 082b1b1..5c50af9 100644
--- a/src/compositor/meta-shaped-texture.c
+++ b/src/compositor/meta-shaped-texture.c
@@ -36,6 +36,7 @@
 
 #include <clutter/clutter.h>
 #include <cogl/cogl.h>
+#include <cogl/cogl-texture-pixmap-x11.h>
 #include <gdk/gdk.h> /* for gdk_rectangle_intersect() */
 #include "meta-cullable.h"
 
@@ -69,8 +70,10 @@ G_DEFINE_TYPE_WITH_CODE (MetaShapedTexture, meta_shaped_texture, CLUTTER_TYPE_AC
 struct _MetaShapedTexturePrivate
 {
   MetaTextureTower *paint_tower;
+  MetaTextureTower *paint_tower_right;
 
   CoglTexture *texture;
+  CoglTexture *texture_right;
   CoglTexture *mask_texture;
 
   cairo_region_t *input_shape_region;
@@ -84,6 +87,7 @@ struct _MetaShapedTexturePrivate
 
   guint tex_width, tex_height;
 
+  guint stereo : 1;
   guint create_mipmaps : 1;
 };
 
@@ -112,8 +116,10 @@ meta_shaped_texture_init (MetaShapedTexture *self)
   priv = self->priv = META_SHAPED_TEXTURE_GET_PRIVATE (self);
 
   priv->paint_tower = meta_texture_tower_new ();
+  priv->paint_tower_right = NULL; /* demand create */
 
   priv->texture = NULL;
+  priv->texture_right = NULL;
   priv->mask_texture = NULL;
   priv->create_mipmaps = TRUE;
 }
@@ -150,11 +156,10 @@ meta_shaped_texture_dispose (GObject *object)
   MetaShapedTexture *self = (MetaShapedTexture *) object;
   MetaShapedTexturePrivate *priv = self->priv;
 
-  if (priv->paint_tower)
-    meta_texture_tower_free (priv->paint_tower);
-  priv->paint_tower = NULL;
-
+  g_clear_pointer (&priv->paint_tower, meta_texture_tower_free);
+  g_clear_pointer (&priv->paint_tower_right, meta_texture_tower_free);
   g_clear_pointer (&priv->texture, cogl_object_unref);
+  g_clear_pointer (&priv->texture_right, cogl_object_unref);
   g_clear_pointer (&priv->opaque_region, cairo_region_destroy);
 
   meta_shaped_texture_set_mask_texture (self, NULL);
@@ -233,49 +238,20 @@ paint_clipped_rectangle (CoglFramebuffer       *fb,
 }
 
 static void
-meta_shaped_texture_paint (ClutterActor *actor)
+paint_texture (MetaShapedTexture *stex,
+              CoglTexture       *paint_tex)
 {
-  MetaShapedTexture *stex = (MetaShapedTexture *) actor;
+  ClutterActor *actor = CLUTTER_ACTOR (stex);
   MetaShapedTexturePrivate *priv = stex->priv;
   guint tex_width, tex_height;
   guchar opacity;
   CoglContext *ctx;
   CoglFramebuffer *fb;
   CoglPipeline *pipeline = NULL;
-  CoglTexture *paint_tex;
   ClutterActorBox alloc;
   cairo_region_t *blended_region = NULL;
   CoglPipelineFilter filter;
 
-  if (priv->clip_region && cairo_region_is_empty (priv->clip_region))
-    return;
-
-  if (!CLUTTER_ACTOR_IS_REALIZED (CLUTTER_ACTOR (stex)))
-    clutter_actor_realize (CLUTTER_ACTOR (stex));
-
-  /* The GL EXT_texture_from_pixmap extension does allow for it to be
-   * used together with SGIS_generate_mipmap, however this is very
-   * rarely supported. Also, even when it is supported there
-   * are distinct performance implications from:
-   *
-   *  - Updating mipmaps that we don't need
-   *  - Having to reallocate pixmaps on the server into larger buffers
-   *
-   * So, we just unconditionally use our mipmap emulation code. If we
-   * wanted to use SGIS_generate_mipmap, we'd have to  query COGL to
-   * see if it was supported (no API currently), and then if and only
-   * if that was the case, set the clutter texture quality to HIGH.
-   * Setting the texture quality to high without SGIS_generate_mipmap
-   * support for TFP textures will result in fallbacks to XGetImage.
-   */
-  if (priv->create_mipmaps)
-    paint_tex = meta_texture_tower_get_paint_texture (priv->paint_tower);
-  else
-    paint_tex = COGL_TEXTURE (priv->texture);
-
-  if (paint_tex == NULL)
-    return;
-
   tex_width = priv->tex_width;
   tex_height = priv->tex_height;
 
@@ -291,8 +267,8 @@ meta_shaped_texture_paint (ClutterActor *actor)
   if (!clutter_actor_is_in_clone_paint (actor) && meta_actor_is_untransformed (actor, NULL, NULL))
     filter = COGL_PIPELINE_FILTER_NEAREST;
 
-  ctx = clutter_backend_get_cogl_context (clutter_get_default_backend ());
   fb = cogl_get_draw_framebuffer ();
+  ctx = clutter_backend_get_cogl_context (clutter_get_default_backend ());
 
   opacity = clutter_actor_get_paint_opacity (actor);
   clutter_actor_get_allocation_box (actor, &alloc);
@@ -416,6 +392,74 @@ meta_shaped_texture_paint (ClutterActor *actor)
 }
 
 static void
+meta_shaped_texture_paint (ClutterActor *actor)
+{
+  MetaShapedTexture *stex = (MetaShapedTexture *) actor;
+  MetaShapedTexturePrivate *priv = stex->priv;
+  CoglFramebuffer *fb;
+  gboolean stereo;
+  CoglTexture *paint_tex;
+  CoglTexture *paint_tex_right;
+
+  if (priv->clip_region && cairo_region_is_empty (priv->clip_region))
+    return;
+
+  if (!CLUTTER_ACTOR_IS_REALIZED (CLUTTER_ACTOR (stex)))
+    clutter_actor_realize (CLUTTER_ACTOR (stex));
+
+  /* The GL EXT_texture_from_pixmap extension does allow for it to be
+   * used together with SGIS_generate_mipmap, however this is very
+   * rarely supported. Also, even when it is supported there
+   * are distinct performance implications from:
+   *
+   *  - Updating mipmaps that we don't need
+   *  - Having to reallocate pixmaps on the server into larger buffers
+   *
+   * So, we just unconditionally use our mipmap emulation code. If we
+   * wanted to use SGIS_generate_mipmap, we'd have to  query COGL to
+   * see if it was supported (no API currently), and then if and only
+   * if that was the case, set the clutter texture quality to HIGH.
+   * Setting the texture quality to high without SGIS_generate_mipmap
+   * support for TFP textures will result in fallbacks to XGetImage.
+   */
+  if (priv->create_mipmaps)
+    paint_tex = meta_texture_tower_get_paint_texture (priv->paint_tower);
+  else
+    paint_tex = COGL_TEXTURE (priv->texture);
+
+  fb = cogl_get_draw_framebuffer ();
+
+  stereo = priv->stereo && cogl_framebuffer_get_is_stereo (fb);
+
+  if (stereo)
+    {
+      if (priv->create_mipmaps)
+       paint_tex_right = meta_texture_tower_get_paint_texture (priv->paint_tower_right);
+      else
+       paint_tex_right = COGL_TEXTURE (priv->texture_right);
+    }
+  else
+    paint_tex_right = NULL;
+
+  if (paint_tex != NULL)
+    {
+      if (stereo)
+       cogl_framebuffer_set_stereo_mode (fb, COGL_STEREO_LEFT);
+      else
+       cogl_framebuffer_set_stereo_mode (fb, COGL_STEREO_BOTH);
+
+      paint_texture (stex, paint_tex);
+    }
+
+  if (paint_tex_right != NULL)
+    {
+      cogl_framebuffer_set_stereo_mode (fb, COGL_STEREO_RIGHT);
+      paint_texture (stex, paint_tex_right);
+      cogl_framebuffer_set_stereo_mode (fb, COGL_STEREO_BOTH);
+    }
+}
+
+static void
 meta_shaped_texture_pick (ClutterActor       *actor,
                          const ClutterColor *color)
 {
@@ -570,6 +614,12 @@ meta_shaped_texture_set_create_mipmaps (MetaShapedTexture *stex,
       priv->create_mipmaps = create_mipmaps;
       base_texture = create_mipmaps ? priv->texture : NULL;
       meta_texture_tower_set_base_texture (priv->paint_tower, base_texture);
+
+      if (priv->stereo)
+       {
+         base_texture = create_mipmaps ? priv->texture_right : NULL;
+         meta_texture_tower_set_base_texture (priv->paint_tower_right, base_texture);
+       }
     }
 }
 
@@ -668,6 +718,8 @@ meta_shaped_texture_update_area (MetaShapedTexture *stex,
     return FALSE;
 
   meta_texture_tower_update_area (priv->paint_tower, x, y, width, height);
+  if (priv->stereo)
+    meta_texture_tower_update_area (priv->paint_tower_right, x, y, width, height);
 
   unobscured_region = effective_unobscured_region (stex);
   if (unobscured_region)
@@ -701,7 +753,8 @@ meta_shaped_texture_update_area (MetaShapedTexture *stex,
 
 static void
 set_cogl_texture (MetaShapedTexture *stex,
-                  CoglTexture       *cogl_tex)
+                  CoglTexture       *cogl_tex,
+                 gboolean           stereo)
 {
   MetaShapedTexturePrivate *priv;
   guint width, height;
@@ -712,8 +765,23 @@ set_cogl_texture (MetaShapedTexture *stex,
 
   if (priv->texture != NULL)
     cogl_object_unref (priv->texture);
+  if (priv->texture_right != NULL)
+    cogl_object_unref (priv->texture_right);
+
+  priv->stereo = stereo;
 
   priv->texture = cogl_tex;
+  if (priv->stereo)
+    {
+      priv->texture_right = cogl_texture_pixmap_x11_create_right_texture ((CoglTexturePixmapX11 *)cogl_tex);
+      if (priv->paint_tower_right == NULL)
+       priv->paint_tower_right = meta_texture_tower_new ();
+    }
+  else
+    {
+      priv->texture_right = NULL;
+      g_clear_pointer (&priv->paint_tower_right, meta_texture_tower_free);
+    }
 
   if (cogl_tex != NULL)
     {
@@ -743,7 +811,11 @@ set_cogl_texture (MetaShapedTexture *stex,
    * damage. */
 
   if (priv->create_mipmaps)
-    meta_texture_tower_set_base_texture (priv->paint_tower, cogl_tex);
+    {
+      meta_texture_tower_set_base_texture (priv->paint_tower, cogl_tex);
+      if (priv->stereo)
+       meta_texture_tower_set_base_texture (priv->paint_tower_right, priv->texture_right);
+    }
 }
 
 /**
@@ -753,11 +825,12 @@ set_cogl_texture (MetaShapedTexture *stex,
  */
 void
 meta_shaped_texture_set_texture (MetaShapedTexture *stex,
-                                 CoglTexture       *texture)
+                                 CoglTexture       *texture,
+                                gboolean           stereo)
 {
   g_return_if_fail (META_IS_SHAPED_TEXTURE (stex));
 
-  set_cogl_texture (stex, texture);
+  set_cogl_texture (stex, texture, stereo);
 }
 
 /**
diff --git a/src/compositor/meta-window-actor-private.h b/src/compositor/meta-window-actor-private.h
index 75a19e1..811f3e8 100644
--- a/src/compositor/meta-window-actor-private.h
+++ b/src/compositor/meta-window-actor-private.h
@@ -58,4 +58,11 @@ void     meta_window_actor_queue_frame_drawn   (MetaWindowActor *self,
 void meta_window_actor_effect_completed (MetaWindowActor *actor,
                                          gulong           event);
 
+void     meta_window_actor_stereo_notify (MetaWindowActor *actor,
+                                          gboolean         stereo_tree);
+
+gboolean meta_window_actor_is_stereo (MetaWindowActor *actor);
+
+void meta_window_actor_detach (MetaWindowActor *self);
+
 #endif /* META_WINDOW_ACTOR_PRIVATE_H */
diff --git a/src/compositor/meta-window-actor.c b/src/compositor/meta-window-actor.c
index 576c103..aedd921 100644
--- a/src/compositor/meta-window-actor.c
+++ b/src/compositor/meta-window-actor.c
@@ -94,6 +94,7 @@ struct _MetaWindowActorPrivate
 
   guint                    visible                : 1;
   guint                    argb32                 : 1;
+  guint                    stereo                 : 1;
   guint                    disposed               : 1;
   guint             redecorating           : 1;
 
@@ -157,7 +158,6 @@ static gboolean meta_window_actor_get_paint_volume (ClutterActor       *actor,
                                                     ClutterPaintVolume *volume);
 
 
-static void     meta_window_actor_detach     (MetaWindowActor *self);
 static gboolean meta_window_actor_has_shadow (MetaWindowActor *self);
 
 static void meta_window_actor_handle_updates (MetaWindowActor *self);
@@ -318,6 +318,9 @@ meta_window_actor_constructed (GObject *object)
   if (format && format->type == PictTypeDirect && format->direct.alphaMask)
     priv->argb32 = TRUE;
 
+  priv->stereo = meta_compositor_window_is_stereo (screen, xwindow);
+  meta_compositor_select_stereo_notify (screen, xwindow);
+
   if (!priv->actor)
     {
       priv->actor = meta_shaped_texture_new ();
@@ -1130,7 +1133,7 @@ meta_window_actor_effect_completed (MetaWindowActor *self,
  * when the window is unmapped or when we want to update to a new
  * pixmap for a new size.
  */
-static void
+void
 meta_window_actor_detach (MetaWindowActor *self)
 {
   MetaWindowActorPrivate *priv     = self->priv;
@@ -1145,7 +1148,7 @@ meta_window_actor_detach (MetaWindowActor *self)
    * you are supposed to be able to free a GLXPixmap after freeing the underlying
    * pixmap, but it certainly doesn't work with current DRI/Mesa
    */
-  meta_shaped_texture_set_texture (META_SHAPED_TEXTURE (priv->actor), NULL);
+  meta_shaped_texture_set_texture (META_SHAPED_TEXTURE (priv->actor), NULL, FALSE);
   cogl_flush();
 
   XFreePixmap (xdisplay, priv->back_pixmap);
@@ -1669,11 +1672,15 @@ check_needs_pixmap (MetaWindowActor *self)
         meta_shaped_texture_set_create_mipmaps (META_SHAPED_TEXTURE (priv->actor),
                                                 FALSE);
 
-      texture = COGL_TEXTURE (cogl_texture_pixmap_x11_new (ctx, priv->back_pixmap, FALSE, NULL));
+      if (priv->stereo)
+        texture = COGL_TEXTURE (cogl_texture_pixmap_x11_new_stereo (ctx, priv->back_pixmap, FALSE, NULL));
+      else
+        texture = COGL_TEXTURE (cogl_texture_pixmap_x11_new (ctx, priv->back_pixmap, FALSE, NULL));
+
       if (G_UNLIKELY (!cogl_texture_pixmap_x11_is_using_tfp_extension (COGL_TEXTURE_PIXMAP_X11 (texture))))
         g_warning ("NOTE: Not using GLX TFP!\n");
 
-      meta_shaped_texture_set_texture (META_SHAPED_TEXTURE (priv->actor), texture);
+      meta_shaped_texture_set_texture (META_SHAPED_TEXTURE (priv->actor), texture, priv->stereo);
     }
 
   priv->needs_pixmap = FALSE;
@@ -2350,3 +2357,20 @@ meta_window_actor_set_updates_frozen (MetaWindowActor *self,
         meta_window_actor_thaw (self);
     }
 }
+
+void
+meta_window_actor_stereo_notify (MetaWindowActor *self,
+                                 gboolean         stereo_tree)
+{
+  MetaWindowActorPrivate *priv = self->priv;
+  MetaWindow *window = priv->window;
+
+  priv->stereo = stereo_tree;
+  meta_window_actor_detach (self);
+}
+
+gboolean
+meta_window_actor_is_stereo (MetaWindowActor *self)
+{
+  return self->priv->stereo;
+}
diff --git a/src/core/stereo.c b/src/core/stereo.c
new file mode 100644
index 0000000..fae4d3d
--- /dev/null
+++ b/src/core/stereo.c
@@ -0,0 +1,144 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+
+/*
+ * SECTION:stereo
+ * @short_description: Keep track of whether we are a stereo compositor
+ *
+ * With GLX, we need to use a different GL context for stereo and
+ * non-stereo support. Support for multiple GL contexts is unfinished
+ * in Cogl and entirely lacking in Clutter, so it's by far easier
+ * to just restart Mutter when we detect a stereo window.
+ *
+ * A property _MUTTER_ENABLE_STEREO is maintained on the root window
+ * to know whether we should initialize clutter for stereo or not.
+ * When the presence or absence of stereo windows mismatches the
+ * stereo-enabled state for a sufficiently long period of time,
+ * we restart Mutter.
+ */
+
+/*
+ * Copyright (C) 2014 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+
+#include <clutter/x11/clutter-x11.h>
+#include <gio/gunixinputstream.h>
+#include <X11/Xatom.h>
+
+#include <meta/main.h>
+#include "ui.h"
+#include "util-private.h"
+#include "display-private.h"
+#include "stereo.h"
+
+static guint stereo_switch_id = 0;
+static gboolean stereo_enabled = FALSE;
+/* -1 so the first time meta_stereo_set_have_stereo_windows() is called
+ * we avoid the short-circuit and set up a timeout to restart
+ * if necessary */
+static gboolean stereo_have_windows = (gboolean)-1;
+static gboolean stereo_restart = FALSE;
+
+#define STEREO_ENABLE_WAIT 1000
+#define STEREO_DISABLE_WAIT 5000
+
+void
+meta_stereo_init (void)
+{
+  Display *xdisplay = meta_ui_get_display ();
+  Window root = DefaultRootWindow (xdisplay);
+  Atom atom_enable_stereo = XInternAtom (xdisplay, "_MUTTER_ENABLE_STEREO", False);
+  Atom type;
+  int format;
+  unsigned long n_items, bytes_after;
+  guchar *data;
+
+  XGetWindowProperty (xdisplay, root, atom_enable_stereo,
+                      0, 1, False, XA_INTEGER,
+                      &type, &format, &n_items, &bytes_after, &data);
+  if (type == XA_INTEGER)
+    {
+      if (format == 32 && n_items == 1 && bytes_after == 0)
+        {
+          stereo_enabled = *(long *)data;
+        }
+      else
+        {
+          meta_warning ("Bad value for _MUTTER_ENABLE_STEREO property\n");
+        }
+
+      XFree (data);
+    }
+  else if (type != None)
+    {
+      meta_warning ("Bad type for _MUTTER_ENABLE_STEREO property\n");
+    }
+
+  meta_verbose ("On startup, _MUTTER_ENABLE_STEREO=%s",
+                stereo_enabled ? "yes" : "no");
+  clutter_x11_set_use_stereo_stage (stereo_enabled);
+}
+
+static gboolean
+meta_stereo_switch (gpointer data)
+{
+  stereo_switch_id = 0;
+  stereo_restart = TRUE;
+
+  meta_restart (stereo_have_windows ?
+                _("Enabling stereo...") :
+                _("Disabling stereo..."));
+
+  return FALSE;
+}
+
+void
+meta_stereo_set_have_stereo_windows (gboolean have_windows)
+{
+  have_windows = have_windows != FALSE;
+
+  if (!stereo_restart && have_windows != stereo_have_windows)
+    {
+      MetaDisplay *display = meta_get_display ();
+      Display *xdisplay = meta_display_get_xdisplay (display);
+      Window root = DefaultRootWindow (xdisplay);
+      Atom atom_enable_stereo = XInternAtom (xdisplay, "_MUTTER_ENABLE_STEREO", False);
+      long value;
+
+      stereo_have_windows = have_windows;
+
+      if (stereo_have_windows)
+        meta_verbose ("Detected stereo windows\n");
+      else
+        meta_verbose ("No stereo windows detected\n");
+
+      value = stereo_have_windows;
+      XChangeProperty (xdisplay, root,
+                       atom_enable_stereo, XA_INTEGER, 32,
+                       PropModeReplace, (guchar *)&value, 1);
+
+      if (stereo_switch_id != 0)
+        {
+          g_source_remove (stereo_switch_id);
+          stereo_switch_id = 0;
+        }
+
+      if (stereo_have_windows != stereo_enabled)
+        stereo_switch_id = g_timeout_add (stereo_have_windows ? STEREO_ENABLE_WAIT : STEREO_DISABLE_WAIT,
+                                          meta_stereo_switch, NULL);
+    }
+}
diff --git a/src/core/stereo.h b/src/core/stereo.h
new file mode 100644
index 0000000..ccd1d70
--- /dev/null
+++ b/src/core/stereo.h
@@ -0,0 +1,28 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+
+/*
+ * Copyright (C) 2014 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef META_STEREO_H
+#define META_STEREO_H
+
+void     meta_stereo_init                    (void);
+void     meta_stereo_set_have_stereo_windows (gboolean have_windows);
+gboolean meta_stereo_is_restart              (void);
+void     meta_stereo_finish_restart          (void);
+
+#endif


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]