[mutter/wip/gcampax/background: 59/68] Reintroduce background crossfading



commit 3e723caabd164e709afeaea4cad480b775c8ff9a
Author: Giovanni Campagna <gcampagna src gnome org>
Date:   Sun Nov 11 00:16:48 2012 +0100

    Reintroduce background crossfading
    
    ... but do it right, using Clutter animation framework for vblank syncing
    and Cogl to generate GLSL shaders that do the hard work of blending the
    old and the new background in HW.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=682427

 src/compositor/cogl-utils.c            |  116 ++++++++--------
 src/compositor/cogl-utils.h            |   10 +-
 src/compositor/compositor.c            |   15 --
 src/compositor/meta-background-actor.c |  236 +++++++++++++++++++++++++-------
 4 files changed, 247 insertions(+), 130 deletions(-)
---
diff --git a/src/compositor/cogl-utils.c b/src/compositor/cogl-utils.c
index 5dbe3df..509d2d0 100644
--- a/src/compositor/cogl-utils.c
+++ b/src/compositor/cogl-utils.c
@@ -23,47 +23,8 @@
 
 #include "cogl-utils.h"
 
-/**
- * meta_create_color_texture_4ub:
- * @red:
- * @green:
- * @blue:
- * @alpha:
- * @flags: Optional flags for the texture, or %COGL_TEXTURE_NONE;
- *   %COGL_TEXTURE_NO_SLICING is useful if the texture will be
- *   repeated to create a constant color fill, since hardware
- *   repeat can't be used for a sliced texture.
- *
- * Creates a texture that is a single pixel with the specified
- * unpremultiplied color components.
- *
- * Return value: (transfer full): a newly created Cogl texture
- */
-CoglHandle
-meta_create_color_texture_4ub (guint8           red,
-                               guint8           green,
-                               guint8           blue,
-                               guint8           alpha,
-                               CoglTextureFlags flags)
-{
-  CoglColor color;
-  guint8 pixel[4];
-
-  cogl_color_set_from_4ub (&color, red, green, blue, alpha);
-  cogl_color_premultiply (&color);
-
-  pixel[0] = cogl_color_get_red_byte (&color);
-  pixel[1] = cogl_color_get_green_byte (&color);
-  pixel[2] = cogl_color_get_blue_byte (&color);
-  pixel[3] = cogl_color_get_alpha_byte (&color);
-
-  return cogl_texture_new_from_data (1, 1,
-                                     flags,
-                                     COGL_PIXEL_FORMAT_RGBA_8888_PRE,
-                                     COGL_PIXEL_FORMAT_ANY,
-                                     4, pixel);
-}
-
+#define CLUTTER_ENABLE_EXPERIMENTAL_API
+#include <clutter/clutter.h>
 
 /* Based on gnome-shell/src/st/st-private.c:_st_create_texture_material.c */
 
@@ -79,32 +40,69 @@ meta_create_color_texture_4ub (guint8           red,
  *
  * Return value: (transfer full): a newly created Cogl material
  */
-CoglHandle
+CoglPipeline *
 meta_create_texture_material (CoglHandle src_texture)
 {
-  static CoglHandle texture_material_template = COGL_INVALID_HANDLE;
-  CoglHandle material;
-
-  /* We use a material that has a dummy texture as a base for all
-     texture materials. The idea is that only the Cogl texture object
-     would be different in the children so it is likely that Cogl will
-     be able to share GL programs between all the textures. */
-  if (G_UNLIKELY (texture_material_template == COGL_INVALID_HANDLE))
-    {
-      CoglHandle dummy_texture;
+  static CoglPipeline *texture_material_template = NULL;
+  CoglPipeline *material;
 
-      dummy_texture = meta_create_color_texture_4ub (0xff, 0xff, 0xff, 0xff,
-                                                     COGL_TEXTURE_NONE);
+  if (G_UNLIKELY (texture_material_template == NULL))
+    {
+      ClutterBackend *backend = clutter_get_default_backend ();
+      CoglContext *context = clutter_backend_get_cogl_context (backend);
 
-      texture_material_template = cogl_material_new ();
-      cogl_material_set_layer (texture_material_template, 0, dummy_texture);
-      cogl_handle_unref (dummy_texture);
+      texture_material_template = cogl_pipeline_new (context);
+      cogl_pipeline_set_layer_null_texture (texture_material_template,
+                                            0, COGL_TEXTURE_TYPE_2D);
     }
 
-  material = cogl_material_copy (texture_material_template);
+  material = cogl_pipeline_copy (texture_material_template);
 
   if (src_texture != COGL_INVALID_HANDLE)
-    cogl_material_set_layer (material, 0, src_texture);
+    cogl_pipeline_set_layer_texture (material, 0, src_texture);
+
+  return material;
+}
+
+/**
+ * meta_create_crossfade_material:
+ * @src_texture_0: (allow-none): the texture to crossfade from
+ * @src_texture_1: (allow-none): the texture to crossfade to
+ *
+ * Creates a material with two layers, using a combine constant to
+ * crossfade between them.
+ *
+ * Return value: (transfer full): a newly created Cogl material
+ */
+CoglPipeline *
+meta_create_crossfade_material (CoglHandle src_texture_0,
+                                CoglHandle src_texture_1)
+{
+  static CoglPipeline *texture_material_template = NULL;
+  CoglPipeline *material;
+
+  if (G_UNLIKELY (texture_material_template == NULL))
+    {
+      ClutterBackend *backend = clutter_get_default_backend ();
+      CoglContext *context = clutter_backend_get_cogl_context (backend);
+
+      texture_material_template = cogl_pipeline_new (context);
+
+      cogl_pipeline_set_layer_null_texture (texture_material_template,
+                                            0, COGL_TEXTURE_TYPE_2D);
+      cogl_pipeline_set_layer_null_texture (texture_material_template,
+                                            1, COGL_TEXTURE_TYPE_2D);
+      cogl_pipeline_set_layer_combine (texture_material_template,
+                                       1, "RGBA = INTERPOLATE (TEXTURE, PREVIOUS, CONSTANT[A])",
+                                       NULL);
+    }
+
+  material = cogl_pipeline_copy (texture_material_template);
+
+  if (src_texture_0 != COGL_INVALID_HANDLE)
+    cogl_pipeline_set_layer_texture (material, 0, src_texture_0);
+  if (src_texture_1 != COGL_INVALID_HANDLE)
+    cogl_pipeline_set_layer_texture (material, 1, src_texture_1);
 
   return material;
 }
diff --git a/src/compositor/cogl-utils.h b/src/compositor/cogl-utils.h
index 8d6742e..b13b16f 100644
--- a/src/compositor/cogl-utils.h
+++ b/src/compositor/cogl-utils.h
@@ -23,13 +23,11 @@
 #ifndef __META_COGL_UTILS_H__
 #define __META_COGL_UTILS_H__
 
+#define COGL_ENABLE_EXPERIMENTAL_API
 #include <cogl/cogl.h>
 
-CoglHandle meta_create_color_texture_4ub (guint8           red,
-                                          guint8           green,
-                                          guint8           blue,
-                                          guint8           alpha,
-                                          CoglTextureFlags flags);
-CoglHandle meta_create_texture_material  (CoglHandle src_texture);
+CoglPipeline *meta_create_texture_material   (CoglHandle src_texture);
+CoglPipeline *meta_create_crossfade_material (CoglHandle src_texture_0,
+                                              CoglHandle src_texture_1);
 
 #endif /* __META_COGL_UTILS_H__ */
diff --git a/src/compositor/compositor.c b/src/compositor/compositor.c
index dabe59e..f8a58ce 100644
--- a/src/compositor/compositor.c
+++ b/src/compositor/compositor.c
@@ -117,21 +117,6 @@ process_property_notify (MetaCompositor	*compositor,
 {
   MetaWindowActor *window_actor;
 
-  if (event->atom == compositor->atom_x_root_pixmap)
-    {
-      GSList *l;
-
-      for (l = meta_display_get_screens (compositor->display); l; l = l->next)
-        {
-	  MetaScreen  *screen = l->data;
-          if (event->window == meta_screen_get_xroot (screen))
-            {
-              meta_background_actor_update (screen);
-              return;
-            }
-        }
-    }
-
   if (window == NULL)
     return;
 
diff --git a/src/compositor/meta-background-actor.c b/src/compositor/meta-background-actor.c
index 558e953..3c2134a 100644
--- a/src/compositor/meta-background-actor.c
+++ b/src/compositor/meta-background-actor.c
@@ -34,6 +34,8 @@
 #include <meta/errors.h>
 #include "meta-background-actor-private.h"
 
+#define CROSSFADE_DURATION 1000
+
 /* We allow creating multiple MetaBackgroundActors for the same MetaScreen to
  * allow different rendering options to be set for different copies.
  * But we want to share the same underlying CoglTexture for efficiency and
@@ -55,6 +57,7 @@ struct _MetaScreenBackground
 
   float texture_width;
   float texture_height;
+  CoglTexture *old_texture;
   CoglTexture *texture;
   CoglMaterialWrapMode wrap_mode;
 
@@ -64,10 +67,14 @@ struct _MetaScreenBackground
 struct _MetaBackgroundActorPrivate
 {
   MetaScreenBackground *background;
+  CoglPipeline *single_pipeline;
+  CoglPipeline *crossfade_pipeline;
   CoglPipeline *pipeline;
 
   cairo_region_t *visible_region;
   float dim_factor;
+  float crossfade_progress;
+  guint is_crossfading : 1;
 };
 
 enum
@@ -75,6 +82,7 @@ enum
   PROP_0,
 
   PROP_DIM_FACTOR,
+  PROP_CROSSFADE_PROGRESS,
 
   PROP_LAST
 };
@@ -83,6 +91,7 @@ static GParamSpec *obj_props[PROP_LAST];
 
 G_DEFINE_TYPE (MetaBackgroundActor, meta_background_actor, CLUTTER_TYPE_ACTOR);
 
+static void clear_old_texture          (MetaScreenBackground *background);
 static void set_texture                (MetaScreenBackground *background,
                                         CoglHandle            texture);
 
@@ -150,19 +159,86 @@ meta_screen_background_get (MetaScreen *screen)
 }
 
 static void
-update_wrap_mode_of_actor (MetaBackgroundActor *self)
+update_actor_pipeline (MetaBackgroundActor *self,
+                       gboolean             crossfade)
 {
   MetaBackgroundActorPrivate *priv = self->priv;
 
-  cogl_pipeline_set_layer_wrap_mode (priv->pipeline, 0, priv->background->wrap_mode);
+  if (crossfade)
+    {
+      priv->pipeline = priv->crossfade_pipeline;
+      priv->is_crossfading = TRUE;
+
+      cogl_pipeline_set_layer_texture (priv->pipeline, 0, priv->background->old_texture);
+      cogl_pipeline_set_layer_wrap_mode (priv->pipeline, 0, priv->background->wrap_mode);
+
+      cogl_pipeline_set_layer_texture (priv->pipeline, 1, priv->background->texture);
+      cogl_pipeline_set_layer_wrap_mode (priv->pipeline, 1, priv->background->wrap_mode);
+    }
+  else
+    {
+      priv->pipeline = priv->single_pipeline;
+      priv->is_crossfading = FALSE;
+
+      cogl_pipeline_set_layer_texture (priv->pipeline, 0, priv->background->texture);
+      cogl_pipeline_set_layer_wrap_mode (priv->pipeline, 0, priv->background->wrap_mode);
+    }
+
+  clutter_actor_queue_redraw (CLUTTER_ACTOR (self));
 }
 
 static void
-update_wrap_mode (MetaScreenBackground *background)
+crossfade_completed (ClutterTimeline     *timeline,
+                     MetaBackgroundActor *actor)
+{
+  clear_old_texture (actor->priv->background);
+  update_actor_pipeline (actor, FALSE);
+}
+
+static void
+clear_old_texture (MetaScreenBackground *background)
+{
+  if (background->old_texture != COGL_INVALID_HANDLE)
+    {
+      cogl_handle_unref (background->old_texture);
+      background->old_texture = COGL_INVALID_HANDLE;
+    }
+}
+
+static void
+set_texture (MetaScreenBackground *background,
+             CoglHandle            texture)
 {
   GSList *l;
+  gboolean crossfade;
   int width, height;
 
+  if (background->old_texture != COGL_INVALID_HANDLE)
+    {
+      cogl_handle_unref (background->old_texture);
+      background->old_texture = COGL_INVALID_HANDLE;
+    }
+
+  if (texture != COGL_INVALID_HANDLE)
+    {
+      background->old_texture = background->texture;
+      background->texture = cogl_handle_ref (texture);
+    }
+  else if (background->texture != COGL_INVALID_HANDLE)
+    {
+      cogl_handle_unref (background->texture);
+      background->texture = COGL_INVALID_HANDLE;
+    }
+
+  if (texture != COGL_INVALID_HANDLE &&
+      background->old_texture != COGL_INVALID_HANDLE)
+    crossfade = TRUE;
+  else
+    crossfade = FALSE;
+
+  background->texture_width = cogl_texture_get_width (background->texture);
+  background->texture_height = cogl_texture_get_height (background->texture);
+
   meta_screen_get_size (background->screen, &width, &height);
 
   /* We turn off repeating when we have a full-screen pixmap to keep from
@@ -175,53 +251,55 @@ update_wrap_mode (MetaScreenBackground *background)
     background->wrap_mode = COGL_MATERIAL_WRAP_MODE_REPEAT;
 
   for (l = background->actors; l; l = l->next)
-    update_wrap_mode_of_actor (l->data);
-}
-
-static void
-set_texture_on_actor (MetaBackgroundActor *self)
-{
-  MetaBackgroundActorPrivate *priv = self->priv;
-  MetaDisplay *display = meta_screen_get_display (priv->background->screen);
+    {
+      MetaBackgroundActor *actor = l->data;
 
-  /* This may trigger destruction of an old texture pixmap, which, if
-   * the underlying X pixmap is already gone has the tendency to trigger
-   * X errors inside DRI. For safety, trap errors */
-  meta_error_trap_push (display);
-  cogl_pipeline_set_layer_texture (priv->pipeline, 0, priv->background->texture);
-  meta_error_trap_pop (display);
+      update_actor_pipeline (actor, crossfade);
 
-  clutter_actor_queue_redraw (CLUTTER_ACTOR (self));
+      if (crossfade)
+        {
+          ClutterTransition *transition;
+          ClutterInterval *interval;
+
+          interval = clutter_interval_new (G_TYPE_FLOAT, 0.0, 1.0);
+          transition = g_object_new (CLUTTER_TYPE_PROPERTY_TRANSITION,
+                                     "animatable", actor,
+                                     "property-name", "crossfade-progress",
+                                     "interval", interval,
+                                     "remove-on-complete", TRUE,
+                                     "duration", CROSSFADE_DURATION,
+                                     "progress-mode", CLUTTER_EASE_OUT_QUAD,
+                                     NULL);
+
+          g_signal_connect_object (transition, "completed",
+                                   G_CALLBACK (crossfade_completed), actor, 0);
+
+          clutter_actor_remove_transition (CLUTTER_ACTOR (actor), "crossfade");
+          clutter_actor_add_transition (CLUTTER_ACTOR (actor), "crossfade",
+                                        transition);
+        }
+    }
 }
 
 static void
-set_texture (MetaScreenBackground *background,
-             CoglHandle            texture)
+update_wrap_mode (MetaScreenBackground *background)
 {
-  MetaDisplay *display = meta_screen_get_display (background->screen);
   GSList *l;
+  int width, height;
 
-  /* This may trigger destruction of an old texture pixmap, which, if
-   * the underlying X pixmap is already gone has the tendency to trigger
-   * X errors inside DRI. For safety, trap errors */
-  meta_error_trap_push (display);
-  if (background->texture != COGL_INVALID_HANDLE)
-    {
-      cogl_handle_unref (background->texture);
-      background->texture = COGL_INVALID_HANDLE;
-    }
-  meta_error_trap_pop (display);
-
-  if (texture != COGL_INVALID_HANDLE)
-    background->texture = cogl_handle_ref (texture);
+  meta_screen_get_size (background->screen, &width, &height);
 
-  background->texture_width = cogl_texture_get_width (background->texture);
-  background->texture_height = cogl_texture_get_height (background->texture);
+  if (width == background->texture_width && height == background->texture_height)
+    background->wrap_mode = COGL_MATERIAL_WRAP_MODE_CLAMP_TO_EDGE;
+  else
+    background->wrap_mode = COGL_MATERIAL_WRAP_MODE_REPEAT;
 
-  for (l = background->actors; l; l = l->next)
-    set_texture_on_actor (l->data);
+  for (l = background->actors; l != NULL; l++)
+    {
+      MetaBackgroundActor *actor = l->data;
 
-  update_wrap_mode (background);
+      update_actor_pipeline (actor, actor->priv->is_crossfading);
+    }
 }
 
 static inline void
@@ -248,7 +326,8 @@ meta_background_actor_dispose (GObject *object)
       priv->background = NULL;
     }
 
-  g_clear_pointer(&priv->pipeline, cogl_object_unref);
+  g_clear_pointer(&priv->single_pipeline, cogl_object_unref);
+  g_clear_pointer(&priv->crossfade_pipeline, cogl_object_unref);
 
   G_OBJECT_CLASS (meta_background_actor_parent_class)->dispose (object);
 }
@@ -298,6 +377,7 @@ meta_background_actor_paint (ClutterActor *actor)
   guint8 opacity = clutter_actor_get_paint_opacity (actor);
   guint8 color_component;
   int width, height;
+  CoglColor crossfade_color;
 
   meta_background_ensure_rendered (priv->background);
 
@@ -311,6 +391,17 @@ meta_background_actor_paint (ClutterActor *actor)
                               color_component,
                               opacity);
 
+  if (priv->is_crossfading)
+    {
+      cogl_color_init_from_4f (&crossfade_color,
+                               priv->crossfade_progress,
+                               priv->crossfade_progress,
+                               priv->crossfade_progress,
+                               priv->crossfade_progress);
+      cogl_pipeline_set_layer_combine_constant (priv->pipeline,
+                                                1, &crossfade_color);
+    }
+
   cogl_set_source (priv->pipeline);
 
   if (priv->visible_region)
@@ -358,6 +449,22 @@ meta_background_actor_get_paint_volume (ClutterActor       *actor,
 }
 
 static void
+meta_background_actor_set_crossfade_progress (MetaBackgroundActor *self,
+                                              gfloat               crossfade_progress)
+{
+  MetaBackgroundActorPrivate *priv = self->priv;
+
+  if (priv->crossfade_progress == crossfade_progress)
+    return;
+
+  priv->crossfade_progress = crossfade_progress;
+
+  clutter_actor_queue_redraw (CLUTTER_ACTOR (self));
+
+  g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_CROSSFADE_PROGRESS]);
+}
+
+static void
 meta_background_actor_set_dim_factor (MetaBackgroundActor *self,
                                       gfloat               dim_factor)
 {
@@ -387,6 +494,8 @@ meta_background_actor_get_property(GObject         *object,
     case PROP_DIM_FACTOR:
       g_value_set_float (value, priv->dim_factor);
       break;
+    case PROP_CROSSFADE_PROGRESS:
+      g_value_set_float (value, priv->crossfade_progress);
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -406,6 +515,9 @@ meta_background_actor_set_property(GObject         *object,
     case PROP_DIM_FACTOR:
       meta_background_actor_set_dim_factor (self, g_value_get_float (value));
       break;
+    case PROP_CROSSFADE_PROGRESS:
+      meta_background_actor_set_crossfade_progress (self, g_value_get_float (value));
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -443,7 +555,18 @@ meta_background_actor_class_init (MetaBackgroundActorClass *klass)
                               1.0,
                               G_PARAM_READWRITE);
   obj_props[PROP_DIM_FACTOR] = pspec;
-  g_object_class_install_property (object_class, PROP_DIM_FACTOR, pspec);
+
+  /**
+   * MetaBackgroundActor:crossfade-progress: (skip)
+   */
+  pspec = g_param_spec_float ("crossfade-progress",
+                              "", "",
+                              0.0, 1.0,
+                              1.0,
+                              G_PARAM_READWRITE);
+  obj_props[PROP_CROSSFADE_PROGRESS] = pspec;
+
+  g_object_class_install_properties (object_class, PROP_LAST, obj_props);
 }
 
 static void
@@ -479,11 +602,12 @@ meta_background_actor_new_for_screen (MetaScreen *screen)
   priv->background = meta_screen_background_get (screen);
   priv->background->actors = g_slist_prepend (priv->background->actors, self);
 
-  /* A CoglMaterial and a CoglPipeline are the same thing */
-  priv->pipeline = (CoglPipeline*) meta_create_texture_material (NULL);
+  priv->single_pipeline = meta_create_texture_material (priv->background->texture);
+  priv->crossfade_pipeline = meta_create_crossfade_material (priv->background->old_texture,
+                                                             priv->background->texture);
 
-  set_texture_on_actor (self);
-  update_wrap_mode_of_actor (self);
+  if (priv->background->texture != COGL_INVALID_HANDLE)
+    update_actor_pipeline (self, FALSE);
 
   return CLUTTER_ACTOR (self);
 }
@@ -520,7 +644,7 @@ on_background_drawn (GObject      *object,
     }
   else
     {
-      g_warning ("Failed to create background texture from pixmap: %s",
+      g_warning ("Failed to render background: %s",
                  error->message);
       g_error_free (error);
     }
@@ -650,9 +774,17 @@ meta_background_actor_add_glsl_snippet (MetaBackgroundActor *actor,
 
   if (hook == META_SNIPPET_HOOK_VERTEX ||
       hook == META_SNIPPET_HOOK_FRAGMENT)
-    cogl_pipeline_add_snippet (priv->pipeline, snippet);
+    {
+      cogl_pipeline_add_snippet (priv->single_pipeline, snippet);
+      cogl_pipeline_add_snippet (priv->crossfade_pipeline, snippet);
+    }
   else
-    cogl_pipeline_add_layer_snippet (priv->pipeline, 0, snippet);
+    {
+      /* In case of crossfading, apply the snippet only to the new texture.
+         We can't apply it to both because declarations would be doubled. */
+      cogl_pipeline_add_layer_snippet (priv->single_pipeline, 0, snippet);
+      cogl_pipeline_add_layer_snippet (priv->crossfade_pipeline, 1, snippet);
+    }
 
   cogl_object_unref (snippet);
 }
@@ -686,8 +818,12 @@ meta_background_actor_set_uniform_float (MetaBackgroundActor *actor,
 
   priv = actor->priv;
 
-  cogl_pipeline_set_uniform_float (priv->pipeline,
-                                   cogl_pipeline_get_uniform_location (priv->pipeline,
+  cogl_pipeline_set_uniform_float (priv->single_pipeline,
+                                   cogl_pipeline_get_uniform_location (priv->single_pipeline,
+                                                                       uniform_name),
+                                   n_components, count, uniform);
+  cogl_pipeline_set_uniform_float (priv->crossfade_pipeline,
+                                   cogl_pipeline_get_uniform_location (priv->crossfade_pipeline,
                                                                        uniform_name),
                                    n_components, count, uniform);
 }


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