Blob Blame History Raw
From 55f30d3240ad636519e1fcc5a579b7d5cba0ed5f Mon Sep 17 00:00:00 2001
From: "Owen W. Taylor" <otaylor@fishsoup.net>
Date: Thu, 8 May 2014 18:52:09 -0400
Subject: [PATCH] Allow setting up quad-buffer stereo output

Add clutter_x11_set_use_stereo_stage() that can be called
before clutter_init() so that the CoglDisplay we create and all
stages created from that CoglDisplay will be created with a
stereo fbconfig.

This is done in clutter-x11 because of the similarity to the
existing clutter_x11_set_use_argb_visual(), and because it's
not clear without other examples whether the need to have
stereo enabled from before clutter_init() is universal or
somethign specific to GLX.

https://bugzilla.gnome.org/show_bug.cgi?id=732706
---
 clutter/x11/clutter-backend-x11.c | 170 +++++++++++++++++++++++++++++---------
 clutter/x11/clutter-x11.h         |   3 +
 2 files changed, 133 insertions(+), 40 deletions(-)

diff --git a/clutter/x11/clutter-backend-x11.c b/clutter/x11/clutter-backend-x11.c
index b7cfcc4..bba21e7 100644
--- a/clutter/x11/clutter-backend-x11.c
+++ b/clutter/x11/clutter-backend-x11.c
@@ -96,6 +96,7 @@ static const gchar *atom_names[] = {
 static gboolean _no_xevent_retrieval = FALSE;
 static gboolean clutter_enable_xinput = TRUE;
 static gboolean clutter_enable_argb = FALSE;
+static gboolean clutter_enable_stereo = FALSE;
 static Display  *_foreign_dpy = NULL;
 
 /* options */
@@ -706,6 +707,59 @@ clutter_backend_x11_get_renderer (ClutterBackend  *backend,
   return renderer;
 }
 
+static gboolean
+check_onscreen_template (CoglRenderer         *renderer,
+                         CoglSwapChain        *swap_chain,
+                         CoglOnscreenTemplate *onscreen_template,
+                         CoglBool              enable_argb,
+                         CoglBool              enable_stereo,
+                         GError              **error)
+{
+  GError *internal_error = NULL;
+
+  cogl_swap_chain_set_has_alpha (swap_chain, enable_argb);
+  cogl_onscreen_template_set_stereo_enabled (onscreen_template,
+					     clutter_enable_stereo);
+
+  /* cogl_renderer_check_onscreen_template() is actually just a
+   * shorthand for creating a CoglDisplay, and calling
+   * cogl_display_setup() on it, then throwing the display away. If we
+   * could just return that display, then it would be more efficient
+   * not to use cogl_renderer_check_onscreen_template(). However, the
+   * backend API requires that we return an CoglDisplay that has not
+   * yet been setup, so one way or the other we'll have to discard the
+   * first display and make a new fresh one.
+   */
+  if (cogl_renderer_check_onscreen_template (renderer, onscreen_template, &internal_error))
+    {
+      clutter_enable_argb = enable_argb;
+      clutter_enable_stereo = enable_stereo;
+
+      return TRUE;
+    }
+  else
+    {
+      if (enable_argb || enable_stereo) /* More possibilities to try */
+        CLUTTER_NOTE (BACKEND,
+                      "Creation of a CoglDisplay with alpha=%s, stereo=%s failed: %s",
+                      enable_argb ? "enabled" : "disabled",
+                      enable_stereo ? "enabled" : "disabled",
+                      internal_error != NULL
+                        ?  internal_error->message
+                        : "Unknown reason");
+      else
+        g_set_error_literal (error, CLUTTER_INIT_ERROR,
+                             CLUTTER_INIT_ERROR_BACKEND,
+                             internal_error != NULL
+                               ? internal_error->message
+                               : "Creation of a CoglDisplay failed");
+
+      g_clear_error (&internal_error);
+
+      return FALSE;
+    }
+}
+
 static CoglDisplay *
 clutter_backend_x11_get_display (ClutterBackend  *backend,
                                  CoglRenderer    *renderer,
@@ -713,56 +767,38 @@ clutter_backend_x11_get_display (ClutterBackend  *backend,
                                  GError         **error)
 {
   CoglOnscreenTemplate *onscreen_template;
-  GError *internal_error = NULL;
-  CoglDisplay *display;
-  gboolean res;
-
-  CLUTTER_NOTE (BACKEND, "Alpha on Cogl swap chain: %s",
-                clutter_enable_argb ? "enabled" : "disabled");
+  CoglDisplay *display = NULL;
+  gboolean res = FALSE;
 
-  cogl_swap_chain_set_has_alpha (swap_chain, clutter_enable_argb);
+  CLUTTER_NOTE (BACKEND, "Creating CoglDisplay, alpha=%s, stereo=%s",
+                clutter_enable_argb ? "enabled" : "disabled",
+                clutter_enable_stereo ? "enabled" : "disabled");
 
   onscreen_template = cogl_onscreen_template_new (swap_chain);
 
-  res = cogl_renderer_check_onscreen_template (renderer,
-                                               onscreen_template,
-                                               &internal_error);
-  if (!res && clutter_enable_argb)
-    {
-      CLUTTER_NOTE (BACKEND,
-                    "Creation of a context with a ARGB visual failed: %s",
-                    internal_error != NULL ? internal_error->message
-                                           : "Unknown reason");
+  /* It's possible that the current renderer doesn't support transparency
+   * or doesn't support stereo, so we try the different combinations.
+   */
+  if (clutter_enable_argb && clutter_enable_stereo)
+    res = check_onscreen_template (renderer, swap_chain, onscreen_template,
+                                  TRUE, TRUE, error);
 
-      g_clear_error (&internal_error);
+  /* Prioritize stereo over alpha */
+  if (!res && clutter_enable_stereo)
+    res = check_onscreen_template (renderer, swap_chain, onscreen_template,
+                                  FALSE, TRUE, error);
 
-      /* It's possible that the current renderer doesn't support transparency
-       * in a swap_chain so lets see if we can fallback to not having any
-       * transparency...
-       *
-       * XXX: It might be nice to have a CoglRenderer feature we could
-       * explicitly check for ahead of time.
-       */
-      clutter_enable_argb = FALSE;
-      cogl_swap_chain_set_has_alpha (swap_chain, FALSE);
-      res = cogl_renderer_check_onscreen_template (renderer,
-                                                   onscreen_template,
-                                                   &internal_error);
-    }
+  if (!res && clutter_enable_argb)
+    res = check_onscreen_template (renderer, swap_chain, onscreen_template,
+                                  TRUE, FALSE, error);
 
   if (!res)
-    {
-      g_set_error_literal (error, CLUTTER_INIT_ERROR,
-                           CLUTTER_INIT_ERROR_BACKEND,
-                           internal_error->message);
+    res = check_onscreen_template (renderer, swap_chain, onscreen_template,
+                                  FALSE, FALSE, error);
 
-      g_error_free (internal_error);
-      cogl_object_unref (onscreen_template);
+  if (res)
+    display = cogl_display_new (renderer, onscreen_template);
 
-      return NULL;
-    }
-
-  display = cogl_display_new (renderer, onscreen_template);
   cogl_object_unref (onscreen_template);
 
   return display;
@@ -1309,6 +1345,60 @@ clutter_x11_get_use_argb_visual (void)
   return clutter_enable_argb;
 }
 
+/**
+ * clutter_x11_set_use_stereo_stage:
+ * @use_stereo: %TRUE if the stereo stages should be used if possible.
+ *
+ * Sets whether the backend object for Clutter stages, will,
+ * if possible, be created with the ability to support stereo drawing
+ * (drawing separate images for the left and right eyes).
+ *
+ * This function must be called before clutter_init() is called.
+ * During paint callbacks, cogl_framebuffer_is_stereo() can be called
+ * on the framebuffer retrieved by cogl_get_draw_framebuffer() to
+ * determine if stereo support was successfully enabled, and
+ * cogl_framebuffer_set_stereo_mode() to determine which buffers
+ * will be drawn to.
+ *
+ * Note that this function *does not* cause the stage to be drawn
+ * multiple times with different perspective transformations and thus
+ * appear in 3D, it simply enables individual ClutterActors to paint
+ * different images for the left and and right eye.
+ *
+ * Since: 1.20
+ */
+void
+clutter_x11_set_use_stereo_stage (gboolean use_stereo)
+{
+  if (_clutter_context_is_initialized ())
+    {
+      g_warning ("%s() can only be used before calling clutter_init()",
+                 G_STRFUNC);
+      return;
+    }
+
+  CLUTTER_NOTE (BACKEND, "STEREO stages are %s",
+                use_stereo ? "enabled" : "disabled");
+
+  clutter_enable_stereo = use_stereo;
+}
+
+/**
+ * clutter_x11_get_use_stereo_stage:
+ *
+ * Retrieves whether the Clutter X11 backend will create stereo
+ * stages if possible.
+ *
+ * Return value: %TRUE if stereo stages are used if possible
+ *
+ * Since: 1.20
+ */
+gboolean
+clutter_x11_get_use_stereo_stage (void)
+{
+  return clutter_enable_stereo;
+}
+
 XVisualInfo *
 _clutter_backend_x11_get_visual_info (ClutterBackendX11 *backend_x11)
 {
diff --git a/clutter/x11/clutter-x11.h b/clutter/x11/clutter-x11.h
index 6913280..efcd37e 100644
--- a/clutter/x11/clutter-x11.h
+++ b/clutter/x11/clutter-x11.h
@@ -137,6 +137,9 @@ gboolean clutter_x11_has_composite_extension (void);
 void     clutter_x11_set_use_argb_visual (gboolean use_argb);
 gboolean clutter_x11_get_use_argb_visual (void);
 
+void     clutter_x11_set_use_stereo_stage (gboolean use_stereo);
+gboolean clutter_x11_get_use_stereo_stage (void);
+
 Time clutter_x11_get_current_event_time (void);
 
 gint clutter_x11_event_get_key_group (const ClutterEvent *event);
-- 
1.9.3