Blame SOURCES/glx-stereo-support.patch

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