Blame SOURCES/0009-MetaShapedTexture-save-and-restore-textures-on-suspe.patch

776610
From 04b7954c7f04e7348f139fdf5c38e7702b387ee3 Mon Sep 17 00:00:00 2001
776610
From: Ray Strode <rstrode@redhat.com>
776610
Date: Thu, 10 Jan 2019 10:48:02 -0500
776610
Subject: [PATCH 9/9] MetaShapedTexture: save and restore textures on suspend
776610
776610
The proprietary nvidia driver garbles GPU memory on suspend.
776610
776610
In order to workaround that limitation, this commit copies all
776610
textures to host memory on suspend and restores them on resume.
776610
776610
One complication comes from external textures (such as those
776610
given to us by Xwayland for X clients).  We can't just restore
776610
those textures, since they aren't writable.
776610
776610
This commit addresses that complication by keeping a local texture
776610
around for those external textures, and using it instead for parts
776610
of the window that haven't been redrawn since resume.
776610
---
776610
 src/compositor/meta-shaped-texture.c | 494 +++++++++++++++++++++++++--
776610
 src/meta/meta-shaped-texture.h       |   2 +
776610
 2 files changed, 475 insertions(+), 21 deletions(-)
776610
776610
diff --git a/src/compositor/meta-shaped-texture.c b/src/compositor/meta-shaped-texture.c
776610
index d8c250fc9..8de173bf7 100644
776610
--- a/src/compositor/meta-shaped-texture.c
776610
+++ b/src/compositor/meta-shaped-texture.c
776610
@@ -2,166 +2,203 @@
776610
  * Authored By Neil Roberts  <neil@linux.intel.com>
776610
  * and Jasper St. Pierre <jstpierre@mecheye.net>
776610
  *
776610
  * Copyright (C) 2008 Intel Corporation
776610
  * Copyright (C) 2012 Red Hat, Inc.
776610
  *
776610
  * This program is free software; you can redistribute it and/or
776610
  * modify it under the terms of the GNU General Public License as
776610
  * published by the Free Software Foundation; either version 2 of the
776610
  * License, or (at your option) any later version.
776610
  *
776610
  * This program is distributed in the hope that it will be useful, but
776610
  * WITHOUT ANY WARRANTY; without even the implied warranty of
776610
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
776610
  * General Public License for more details.
776610
  *
776610
  * You should have received a copy of the GNU General Public License
776610
  * along with this program; if not, see <http://www.gnu.org/licenses/>.
776610
  */
776610
 
776610
 /**
776610
  * SECTION:meta-shaped-texture
776610
  * @title: MetaShapedTexture
776610
  * @short_description: An actor to draw a masked texture.
776610
  */
776610
 
776610
 #include <config.h>
776610
 
776610
 #include <meta/meta-shaped-texture.h>
776610
 #include "meta-shaped-texture-private.h"
776610
+#include "meta-texture-rectangle.h"
776610
 
776610
 #include <cogl/cogl.h>
776610
 #include <gdk/gdk.h> /* for gdk_rectangle_intersect() */
776610
 
776610
 #include "clutter-utils.h"
776610
 #include "meta-texture-tower.h"
776610
 #include "core/boxes-private.h"
776610
 
776610
 #include "meta-cullable.h"
776610
+#include <meta/meta-backend.h>
776610
 
776610
 static void meta_shaped_texture_dispose  (GObject    *object);
776610
 
776610
 static void meta_shaped_texture_paint (ClutterActor       *actor);
776610
 
776610
 static void meta_shaped_texture_get_preferred_width (ClutterActor *self,
776610
                                                      gfloat        for_height,
776610
                                                      gfloat       *min_width_p,
776610
                                                      gfloat       *natural_width_p);
776610
 
776610
 static void meta_shaped_texture_get_preferred_height (ClutterActor *self,
776610
                                                       gfloat        for_width,
776610
                                                       gfloat       *min_height_p,
776610
                                                       gfloat       *natural_height_p);
776610
 
776610
 static gboolean meta_shaped_texture_get_paint_volume (ClutterActor *self, ClutterPaintVolume *volume);
776610
 
776610
+static void disable_backing_store (MetaShapedTexture *stex);
776610
+
776610
 static void cullable_iface_init (MetaCullableInterface *iface);
776610
 
776610
+static gboolean meta_debug_show_backing_store = FALSE;
776610
+
776610
 G_DEFINE_TYPE_WITH_CODE (MetaShapedTexture, meta_shaped_texture, CLUTTER_TYPE_ACTOR,
776610
                          G_IMPLEMENT_INTERFACE (META_TYPE_CULLABLE, cullable_iface_init));
776610
 
776610
 #define META_SHAPED_TEXTURE_GET_PRIVATE(obj) \
776610
   (G_TYPE_INSTANCE_GET_PRIVATE ((obj), META_TYPE_SHAPED_TEXTURE, \
776610
                                 MetaShapedTexturePrivate))
776610
 
776610
 enum {
776610
   SIZE_CHANGED,
776610
 
776610
   LAST_SIGNAL,
776610
 };
776610
 
776610
 static guint signals[LAST_SIGNAL];
776610
 
776610
+typedef struct
776610
+{
776610
+  CoglTexture *texture;
776610
+  CoglTexture *mask_texture;
776610
+  cairo_surface_t *mask_surface;
776610
+  cairo_region_t *region;
776610
+} MetaTextureBackingStore;
776610
+
776610
 struct _MetaShapedTexturePrivate
776610
 {
776610
   MetaTextureTower *paint_tower;
776610
 
776610
   CoglTexture *texture;
776610
   CoglTexture *mask_texture;
776610
   CoglSnippet *snippet;
776610
 
776610
   CoglPipeline *base_pipeline;
776610
   CoglPipeline *masked_pipeline;
776610
   CoglPipeline *unblended_pipeline;
776610
 
776610
   gboolean is_y_inverted;
776610
 
776610
   /* The region containing only fully opaque pixels */
776610
   cairo_region_t *opaque_region;
776610
 
776610
   /* MetaCullable regions, see that documentation for more details */
776610
   cairo_region_t *clip_region;
776610
   cairo_region_t *unobscured_region;
776610
 
776610
+  /* textures get corrupted on suspend, so save them */
776610
+  cairo_surface_t *saved_base_surface;
776610
+  cairo_surface_t *saved_mask_surface;
776610
+
776610
+  /* We can't just restore external textures, so we need to track
776610
+   * which parts of the external texture are freshly drawn from
776610
+   * the client after corruption, and fill in the rest from our
776610
+   * saved snapshot */
776610
+  MetaTextureBackingStore *backing_store;
776610
+
776610
   guint tex_width, tex_height;
776610
   guint fallback_width, fallback_height;
776610
 
776610
   guint create_mipmaps : 1;
776610
 };
776610
 
776610
 static void
776610
 meta_shaped_texture_class_init (MetaShapedTextureClass *klass)
776610
 {
776610
   GObjectClass *gobject_class = (GObjectClass *) klass;
776610
   ClutterActorClass *actor_class = (ClutterActorClass *) klass;
776610
 
776610
   gobject_class->dispose = meta_shaped_texture_dispose;
776610
 
776610
   actor_class->get_preferred_width = meta_shaped_texture_get_preferred_width;
776610
   actor_class->get_preferred_height = meta_shaped_texture_get_preferred_height;
776610
   actor_class->paint = meta_shaped_texture_paint;
776610
   actor_class->get_paint_volume = meta_shaped_texture_get_paint_volume;
776610
 
776610
   signals[SIZE_CHANGED] = g_signal_new ("size-changed",
776610
                                         G_TYPE_FROM_CLASS (gobject_class),
776610
                                         G_SIGNAL_RUN_LAST,
776610
                                         0,
776610
                                         NULL, NULL, NULL,
776610
                                         G_TYPE_NONE, 0);
776610
 
776610
   g_type_class_add_private (klass, sizeof (MetaShapedTexturePrivate));
776610
+
776610
+  if (g_getenv ("MUTTER_DEBUG_BACKING_STORE"))
776610
+    meta_debug_show_backing_store = TRUE;
776610
 }
776610
 
776610
 static void
776610
 meta_shaped_texture_init (MetaShapedTexture *self)
776610
 {
776610
   MetaShapedTexturePrivate *priv;
776610
+  MetaBackend *backend = meta_get_backend ();
776610
+  ClutterBackend *clutter_backend = clutter_get_default_backend ();
776610
+  CoglContext *cogl_context =
776610
+    clutter_backend_get_cogl_context (clutter_backend);
776610
 
776610
   priv = self->priv = META_SHAPED_TEXTURE_GET_PRIVATE (self);
776610
 
776610
   priv->paint_tower = meta_texture_tower_new ();
776610
 
776610
   priv->texture = NULL;
776610
   priv->mask_texture = NULL;
776610
   priv->create_mipmaps = TRUE;
776610
   priv->is_y_inverted = TRUE;
776610
+
776610
+  if (cogl_has_feature (cogl_context, COGL_FEATURE_ID_UNSTABLE_TEXTURES))
776610
+    {
776610
+      g_signal_connect_object (backend, "suspending", G_CALLBACK (meta_shaped_texture_save), self, G_CONNECT_SWAPPED);
776610
+      g_signal_connect_object (backend, "resuming", G_CALLBACK (meta_shaped_texture_restore), self, G_CONNECT_SWAPPED);
776610
+    }
776610
 }
776610
 
776610
 static void
776610
 set_unobscured_region (MetaShapedTexture *self,
776610
                        cairo_region_t    *unobscured_region)
776610
 {
776610
   MetaShapedTexturePrivate *priv = self->priv;
776610
 
776610
   g_clear_pointer (&priv->unobscured_region, (GDestroyNotify) cairo_region_destroy);
776610
   if (unobscured_region)
776610
     {
776610
       guint width, height;
776610
 
776610
       if (priv->texture)
776610
         {
776610
           width = priv->tex_width;
776610
           height = priv->tex_height;
776610
         }
776610
       else
776610
         {
776610
           width = priv->fallback_width;
776610
           height = priv->fallback_height;
776610
         }
776610
 
776610
       cairo_rectangle_int_t bounds = { 0, 0, width, height };
776610
       priv->unobscured_region = cairo_region_copy (unobscured_region);
776610
       cairo_region_intersect_rectangle (priv->unobscured_region, &bounds);
776610
     }
776610
 }
776610
 
776610
@@ -183,116 +220,183 @@ meta_shaped_texture_reset_pipelines (MetaShapedTexture *stex)
776610
 
776610
   g_clear_pointer (&priv->base_pipeline, cogl_object_unref);
776610
   g_clear_pointer (&priv->masked_pipeline, cogl_object_unref);
776610
   g_clear_pointer (&priv->unblended_pipeline, cogl_object_unref);
776610
 }
776610
 
776610
 static void
776610
 meta_shaped_texture_dispose (GObject *object)
776610
 {
776610
   MetaShapedTexture *self = (MetaShapedTexture *) object;
776610
   MetaShapedTexturePrivate *priv = self->priv;
776610
 
776610
   if (priv->paint_tower)
776610
     meta_texture_tower_free (priv->paint_tower);
776610
   priv->paint_tower = NULL;
776610
 
776610
   g_clear_pointer (&priv->texture, cogl_object_unref);
776610
   g_clear_pointer (&priv->opaque_region, cairo_region_destroy);
776610
 
776610
   meta_shaped_texture_set_mask_texture (self, NULL);
776610
   set_unobscured_region (self, NULL);
776610
   set_clip_region (self, NULL);
776610
 
776610
   meta_shaped_texture_reset_pipelines (self);
776610
 
776610
   g_clear_pointer (&priv->snippet, cogl_object_unref);
776610
 
776610
   G_OBJECT_CLASS (meta_shaped_texture_parent_class)->dispose (object);
776610
 }
776610
 
776610
+static int
776610
+get_layer_indices (MetaShapedTexture *stex,
776610
+                   int               *main_layer_index,
776610
+                   int               *backing_mask_layer_index,
776610
+                   int               *backing_layer_index,
776610
+                   int               *mask_layer_index)
776610
+{
776610
+  MetaShapedTexturePrivate *priv = stex->priv;
776610
+  int next_layer_index = 0;
776610
+
776610
+  if (main_layer_index)
776610
+    *main_layer_index = next_layer_index;
776610
+
776610
+  next_layer_index++;
776610
+
776610
+  if (priv->backing_store)
776610
+    {
776610
+      if (backing_mask_layer_index)
776610
+        *backing_mask_layer_index = next_layer_index;
776610
+      next_layer_index++;
776610
+      if (backing_layer_index)
776610
+        *backing_layer_index = next_layer_index;
776610
+      next_layer_index++;
776610
+    }
776610
+  else
776610
+    {
776610
+      if (backing_mask_layer_index)
776610
+        *backing_mask_layer_index = -1;
776610
+      if (backing_layer_index)
776610
+        *backing_layer_index = -1;
776610
+    }
776610
+
776610
+  if (mask_layer_index)
776610
+    *mask_layer_index = next_layer_index;
776610
+
776610
+  return next_layer_index;
776610
+}
776610
+
776610
 static CoglPipeline *
776610
 get_base_pipeline (MetaShapedTexture *stex,
776610
                    CoglContext       *ctx)
776610
 {
776610
   MetaShapedTexturePrivate *priv = stex->priv;
776610
   CoglPipeline *pipeline;
776610
+  int main_layer_index;
776610
+  int backing_layer_index;
776610
+  int backing_mask_layer_index;
776610
+  int i, number_of_layers;
776610
 
776610
   if (priv->base_pipeline)
776610
     return priv->base_pipeline;
776610
 
776610
   pipeline = cogl_pipeline_new (ctx);
776610
-  cogl_pipeline_set_layer_wrap_mode_s (pipeline, 0,
776610
-                                       COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE);
776610
-  cogl_pipeline_set_layer_wrap_mode_t (pipeline, 0,
776610
-                                       COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE);
776610
-  cogl_pipeline_set_layer_wrap_mode_s (pipeline, 1,
776610
-                                       COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE);
776610
-  cogl_pipeline_set_layer_wrap_mode_t (pipeline, 1,
776610
-                                       COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE);
776610
+
776610
+  number_of_layers = get_layer_indices (stex,
776610
+                                        &main_layer_index,
776610
+                                        &backing_mask_layer_index,
776610
+                                        &backing_layer_index,
776610
+                                        NULL);
776610
+
776610
+  for (i = 0; i < number_of_layers; i++)
776610
+    {
776610
+      cogl_pipeline_set_layer_wrap_mode_s (pipeline, i,
776610
+                                           COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE);
776610
+      cogl_pipeline_set_layer_wrap_mode_t (pipeline, i,
776610
+                                           COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE);
776610
+    }
776610
+
776610
   if (!priv->is_y_inverted)
776610
     {
776610
       CoglMatrix matrix;
776610
 
776610
       cogl_matrix_init_identity (&matrix);
776610
       cogl_matrix_scale (&matrix, 1, -1, 1);
776610
       cogl_matrix_translate (&matrix, 0, -1, 0);
776610
-      cogl_pipeline_set_layer_matrix (pipeline, 0, &matrix);
776610
+      cogl_pipeline_set_layer_matrix (pipeline, main_layer_index, &matrix);
776610
+    }
776610
+
776610
+  if (priv->backing_store)
776610
+    {
776610
+      g_autofree char *backing_description = NULL;
776610
+      cogl_pipeline_set_layer_combine (pipeline, backing_mask_layer_index,
776610
+                                       "RGBA = REPLACE(PREVIOUS)",
776610
+                                       NULL);
776610
+      backing_description = g_strdup_printf ("RGBA = INTERPOLATE(PREVIOUS, TEXTURE_%d, TEXTURE_%d[A])",
776610
+                                             backing_layer_index,
776610
+                                             backing_mask_layer_index);
776610
+      cogl_pipeline_set_layer_combine (pipeline,
776610
+                                       backing_layer_index,
776610
+                                       backing_description,
776610
+                                       NULL);
776610
     }
776610
 
776610
   if (priv->snippet)
776610
-    cogl_pipeline_add_layer_snippet (pipeline, 0, priv->snippet);
776610
+    cogl_pipeline_add_layer_snippet (pipeline, main_layer_index, priv->snippet);
776610
 
776610
   priv->base_pipeline = pipeline;
776610
 
776610
   return priv->base_pipeline;
776610
 }
776610
 
776610
 static CoglPipeline *
776610
 get_unmasked_pipeline (MetaShapedTexture *stex,
776610
                        CoglContext       *ctx)
776610
 {
776610
   return get_base_pipeline (stex, ctx);
776610
 }
776610
 
776610
 static CoglPipeline *
776610
 get_masked_pipeline (MetaShapedTexture *stex,
776610
                      CoglContext       *ctx)
776610
 {
776610
   MetaShapedTexturePrivate *priv = stex->priv;
776610
   CoglPipeline *pipeline;
776610
+  int mask_layer_index;
776610
 
776610
   if (priv->masked_pipeline)
776610
     return priv->masked_pipeline;
776610
 
776610
+  get_layer_indices (stex, NULL, NULL, NULL, &mask_layer_index);
776610
+
776610
   pipeline = cogl_pipeline_copy (get_base_pipeline (stex, ctx));
776610
-  cogl_pipeline_set_layer_combine (pipeline, 1,
776610
+  cogl_pipeline_set_layer_combine (pipeline, mask_layer_index,
776610
                                    "RGBA = MODULATE (PREVIOUS, TEXTURE[A])",
776610
                                    NULL);
776610
 
776610
   priv->masked_pipeline = pipeline;
776610
 
776610
   return pipeline;
776610
 }
776610
 
776610
 static CoglPipeline *
776610
 get_unblended_pipeline (MetaShapedTexture *stex,
776610
                         CoglContext       *ctx)
776610
 {
776610
   MetaShapedTexturePrivate *priv = stex->priv;
776610
   CoglPipeline *pipeline;
776610
   CoglColor color;
776610
 
776610
   if (priv->unblended_pipeline)
776610
     return priv->unblended_pipeline;
776610
 
776610
   pipeline = cogl_pipeline_copy (get_base_pipeline (stex, ctx));
776610
   cogl_color_init_from_4ub (&color, 255, 255, 255, 255);
776610
   cogl_pipeline_set_blend (pipeline,
776610
                            "RGBA = ADD (SRC_COLOR, 0)",
776610
                            NULL);
776610
   cogl_pipeline_set_color (pipeline, &color;;
776610
 
776610
   priv->unblended_pipeline = pipeline;
776610
 
776610
   return pipeline;
776610
 }
776610
@@ -313,105 +417,111 @@ paint_clipped_rectangle (CoglFramebuffer       *fb,
776610
 
776610
   coords[0] = rect->x / (alloc->x2 - alloc->x1);
776610
   coords[1] = rect->y / (alloc->y2 - alloc->y1);
776610
   coords[2] = (rect->x + rect->width) / (alloc->x2 - alloc->x1);
776610
   coords[3] = (rect->y + rect->height) / (alloc->y2 - alloc->y1);
776610
 
776610
   coords[4] = coords[0];
776610
   coords[5] = coords[1];
776610
   coords[6] = coords[2];
776610
   coords[7] = coords[3];
776610
 
776610
   cogl_framebuffer_draw_multitextured_rectangle (fb, pipeline,
776610
                                                  x1, y1, x2, y2,
776610
                                                  &coords[0], 8);
776610
 }
776610
 
776610
 static void
776610
 set_cogl_texture (MetaShapedTexture *stex,
776610
                   CoglTexture       *cogl_tex)
776610
 {
776610
   MetaShapedTexturePrivate *priv;
776610
   guint width, height;
776610
 
776610
   g_return_if_fail (META_IS_SHAPED_TEXTURE (stex));
776610
 
776610
   priv = stex->priv;
776610
 
776610
   if (priv->texture)
776610
     cogl_object_unref (priv->texture);
776610
 
776610
+  g_clear_pointer (&priv->saved_base_surface, cairo_surface_destroy);
776610
+
776610
   priv->texture = cogl_tex;
776610
 
776610
   if (cogl_tex != NULL)
776610
     {
776610
       cogl_object_ref (cogl_tex);
776610
       width = cogl_texture_get_width (COGL_TEXTURE (cogl_tex));
776610
       height = cogl_texture_get_height (COGL_TEXTURE (cogl_tex));
776610
     }
776610
   else
776610
     {
776610
       width = 0;
776610
       height = 0;
776610
     }
776610
 
776610
   if (priv->tex_width != width ||
776610
       priv->tex_height != height)
776610
     {
776610
       priv->tex_width = width;
776610
       priv->tex_height = height;
776610
       meta_shaped_texture_set_mask_texture (stex, NULL);
776610
       clutter_actor_queue_relayout (CLUTTER_ACTOR (stex));
776610
       g_signal_emit (stex, signals[SIZE_CHANGED], 0);
776610
     }
776610
 
776610
   /* NB: We don't queue a redraw of the actor here because we don't
776610
    * know how much of the buffer has changed with respect to the
776610
    * previous buffer. We only queue a redraw in response to surface
776610
    * damage. */
776610
 
776610
   if (priv->create_mipmaps)
776610
     meta_texture_tower_set_base_texture (priv->paint_tower, cogl_tex);
776610
 }
776610
 
776610
 static void
776610
 do_paint (MetaShapedTexture *stex,
776610
           CoglFramebuffer   *fb,
776610
           CoglTexture       *paint_tex,
776610
           cairo_region_t    *clip_region)
776610
 {
776610
   MetaShapedTexturePrivate *priv = stex->priv;
776610
   guint tex_width, tex_height;
776610
   guchar opacity;
776610
   CoglContext *ctx;
776610
   ClutterActorBox alloc;
776610
   CoglPipelineFilter filter;
776610
+  int main_layer_index;
776610
+  int backing_mask_layer_index;
776610
+  int backing_layer_index;
776610
+  int mask_layer_index;
776610
 
776610
   tex_width = priv->tex_width;
776610
   tex_height = priv->tex_height;
776610
 
776610
   if (tex_width == 0 || tex_height == 0) /* no contents yet */
776610
     return;
776610
 
776610
   cairo_rectangle_int_t tex_rect = { 0, 0, tex_width, tex_height };
776610
 
776610
   /* Use nearest-pixel interpolation if the texture is unscaled. This
776610
    * improves performance, especially with software rendering.
776610
    */
776610
 
776610
   filter = COGL_PIPELINE_FILTER_LINEAR;
776610
 
776610
   if (meta_actor_painting_untransformed (fb,
776610
                                          tex_width, tex_height,
776610
                                          NULL, NULL))
776610
     filter = COGL_PIPELINE_FILTER_NEAREST;
776610
 
776610
   ctx = clutter_backend_get_cogl_context (clutter_get_default_backend ());
776610
 
776610
   opacity = clutter_actor_get_paint_opacity (CLUTTER_ACTOR (stex));
776610
   clutter_actor_get_allocation_box (CLUTTER_ACTOR (stex), &alloc);
776610
 
776610
   cairo_region_t *blended_region;
776610
   gboolean use_opaque_region = (priv->opaque_region != NULL && opacity == 255);
776610
 
776610
   if (use_opaque_region)
776610
     {
776610
@@ -420,123 +530,161 @@ do_paint (MetaShapedTexture *stex,
776610
       else
776610
         blended_region = cairo_region_create_rectangle (&tex_rect);
776610
 
776610
       cairo_region_subtract (blended_region, priv->opaque_region);
776610
     }
776610
   else
776610
     {
776610
       if (priv->clip_region != NULL)
776610
         blended_region = cairo_region_reference (priv->clip_region);
776610
       else
776610
         blended_region = NULL;
776610
     }
776610
 
776610
   /* Limit to how many separate rectangles we'll draw; beyond this just
776610
    * fall back and draw the whole thing */
776610
 #define MAX_RECTS 16
776610
 
776610
   if (blended_region != NULL)
776610
     {
776610
       int n_rects = cairo_region_num_rectangles (blended_region);
776610
       if (n_rects > MAX_RECTS)
776610
         {
776610
           /* Fall back to taking the fully blended path. */
776610
           use_opaque_region = FALSE;
776610
 
776610
           cairo_region_destroy (blended_region);
776610
           blended_region = NULL;
776610
         }
776610
     }
776610
 
776610
+  get_layer_indices (stex,
776610
+                     &main_layer_index,
776610
+                     &backing_mask_layer_index,
776610
+                     &backing_layer_index,
776610
+                     &mask_layer_index);
776610
+
776610
   /* First, paint the unblended parts, which are part of the opaque region. */
776610
   if (use_opaque_region)
776610
     {
776610
       CoglPipeline *opaque_pipeline;
776610
       cairo_region_t *region;
776610
       int n_rects;
776610
       int i;
776610
 
776610
       if (priv->clip_region != NULL)
776610
         {
776610
           region = cairo_region_copy (priv->clip_region);
776610
           cairo_region_intersect (region, priv->opaque_region);
776610
         }
776610
       else
776610
         {
776610
           region = cairo_region_reference (priv->opaque_region);
776610
         }
776610
 
776610
       if (!cairo_region_is_empty (region))
776610
         {
776610
           opaque_pipeline = get_unblended_pipeline (stex, ctx);
776610
-          cogl_pipeline_set_layer_texture (opaque_pipeline, 0, paint_tex);
776610
-          cogl_pipeline_set_layer_filters (opaque_pipeline, 0, filter, filter);
776610
+          cogl_pipeline_set_layer_texture (opaque_pipeline, main_layer_index, paint_tex);
776610
+          cogl_pipeline_set_layer_filters (opaque_pipeline, main_layer_index, filter, filter);
776610
+
776610
+          if (priv->backing_store)
776610
+            {
776610
+              cogl_pipeline_set_layer_texture (opaque_pipeline,
776610
+                                               backing_mask_layer_index,
776610
+                                               priv->backing_store->mask_texture);
776610
+              cogl_pipeline_set_layer_filters (opaque_pipeline,
776610
+                                               backing_mask_layer_index,
776610
+                                               filter, filter);
776610
+              cogl_pipeline_set_layer_texture (opaque_pipeline,
776610
+                                               backing_layer_index,
776610
+                                               priv->backing_store->texture);
776610
+              cogl_pipeline_set_layer_filters (opaque_pipeline,
776610
+                                               backing_layer_index,
776610
+                                               filter, filter);
776610
+            }
776610
 
776610
           n_rects = cairo_region_num_rectangles (region);
776610
           for (i = 0; i < n_rects; i++)
776610
             {
776610
               cairo_rectangle_int_t rect;
776610
               cairo_region_get_rectangle (region, i, &rect);
776610
               paint_clipped_rectangle (fb, opaque_pipeline, &rect, &alloc);
776610
             }
776610
         }
776610
 
776610
       cairo_region_destroy (region);
776610
     }
776610
 
776610
   /* Now, go ahead and paint the blended parts. */
776610
 
776610
   /* We have three cases:
776610
    *   1) blended_region has rectangles - paint the rectangles.
776610
    *   2) blended_region is empty - don't paint anything
776610
    *   3) blended_region is NULL - paint fully-blended.
776610
    *
776610
    *   1) and 3) are the times where we have to paint stuff. This tests
776610
    *   for 1) and 3).
776610
    */
776610
   if (blended_region == NULL || !cairo_region_is_empty (blended_region))
776610
     {
776610
       CoglPipeline *blended_pipeline;
776610
 
776610
       if (priv->mask_texture == NULL)
776610
         {
776610
           blended_pipeline = get_unmasked_pipeline (stex, ctx);
776610
         }
776610
       else
776610
         {
776610
           blended_pipeline = get_masked_pipeline (stex, ctx);
776610
-          cogl_pipeline_set_layer_texture (blended_pipeline, 1, priv->mask_texture);
776610
-          cogl_pipeline_set_layer_filters (blended_pipeline, 1, filter, filter);
776610
+          cogl_pipeline_set_layer_texture (blended_pipeline, mask_layer_index, priv->mask_texture);
776610
+          cogl_pipeline_set_layer_filters (blended_pipeline, mask_layer_index, filter, filter);
776610
         }
776610
 
776610
-      cogl_pipeline_set_layer_texture (blended_pipeline, 0, paint_tex);
776610
-      cogl_pipeline_set_layer_filters (blended_pipeline, 0, filter, filter);
776610
+      cogl_pipeline_set_layer_texture (blended_pipeline, main_layer_index, paint_tex);
776610
+      cogl_pipeline_set_layer_filters (blended_pipeline, main_layer_index, filter, filter);
776610
+
776610
+      if (priv->backing_store)
776610
+        {
776610
+          cogl_pipeline_set_layer_texture (blended_pipeline,
776610
+                                           backing_mask_layer_index,
776610
+                                           priv->backing_store->mask_texture);
776610
+          cogl_pipeline_set_layer_filters (blended_pipeline,
776610
+                                           backing_mask_layer_index,
776610
+                                           filter, filter);
776610
+          cogl_pipeline_set_layer_texture (blended_pipeline,
776610
+                                           backing_layer_index,
776610
+                                           priv->backing_store->texture);
776610
+          cogl_pipeline_set_layer_filters (blended_pipeline,
776610
+                                           backing_layer_index,
776610
+                                           filter, filter);
776610
+        }
776610
 
776610
       CoglColor color;
776610
       cogl_color_init_from_4ub (&color, opacity, opacity, opacity, opacity);
776610
       cogl_pipeline_set_color (blended_pipeline, &color;;
776610
 
776610
       if (blended_region != NULL)
776610
         {
776610
           /* 1) blended_region is not empty. Paint the rectangles. */
776610
           int i;
776610
           int n_rects = cairo_region_num_rectangles (blended_region);
776610
 
776610
           for (i = 0; i < n_rects; i++)
776610
             {
776610
               cairo_rectangle_int_t rect;
776610
               cairo_region_get_rectangle (blended_region, i, &rect);
776610
 
776610
               if (!gdk_rectangle_intersect (&tex_rect, &rect, &rect))
776610
                 continue;
776610
 
776610
               paint_clipped_rectangle (fb, blended_pipeline, &rect, &alloc);
776610
             }
776610
         }
776610
       else
776610
         {
776610
           /* 3) blended_region is NULL. Do a full paint. */
776610
           cogl_framebuffer_draw_rectangle (fb, blended_pipeline,
776610
                                            0, 0,
776610
                                            alloc.x2 - alloc.x1,
776610
                                            alloc.y2 - alloc.y1);
776610
         }
776610
@@ -698,110 +846,173 @@ meta_shaped_texture_set_create_mipmaps (MetaShapedTexture *stex,
776610
 					gboolean           create_mipmaps)
776610
 {
776610
   MetaShapedTexturePrivate *priv;
776610
 
776610
   g_return_if_fail (META_IS_SHAPED_TEXTURE (stex));
776610
 
776610
   priv = stex->priv;
776610
 
776610
   create_mipmaps = create_mipmaps != FALSE;
776610
 
776610
   if (create_mipmaps != priv->create_mipmaps)
776610
     {
776610
       CoglTexture *base_texture;
776610
       priv->create_mipmaps = create_mipmaps;
776610
       base_texture = create_mipmaps ? priv->texture : NULL;
776610
       meta_texture_tower_set_base_texture (priv->paint_tower, base_texture);
776610
     }
776610
 }
776610
 
776610
 void
776610
 meta_shaped_texture_set_mask_texture (MetaShapedTexture *stex,
776610
                                       CoglTexture       *mask_texture)
776610
 {
776610
   MetaShapedTexturePrivate *priv;
776610
 
776610
   g_return_if_fail (META_IS_SHAPED_TEXTURE (stex));
776610
 
776610
   priv = stex->priv;
776610
 
776610
   g_clear_pointer (&priv->mask_texture, cogl_object_unref);
776610
+  g_clear_pointer (&priv->saved_mask_surface, cairo_surface_destroy);
776610
 
776610
   if (mask_texture != NULL)
776610
     {
776610
       priv->mask_texture = mask_texture;
776610
       cogl_object_ref (priv->mask_texture);
776610
     }
776610
 
776610
   clutter_actor_queue_redraw (CLUTTER_ACTOR (stex));
776610
 }
776610
 
776610
 gboolean
776610
 meta_shaped_texture_is_obscured (MetaShapedTexture *self)
776610
 {
776610
   cairo_region_t *unobscured_region = effective_unobscured_region (self);
776610
 
776610
   if (unobscured_region)
776610
     return cairo_region_is_empty (unobscured_region);
776610
   else
776610
     return FALSE;
776610
 }
776610
 
776610
+static void
776610
+meta_texture_backing_store_redraw_mask (MetaTextureBackingStore *backing_store)
776610
+{
776610
+  CoglError *error = NULL;
776610
+
776610
+  if (!cogl_texture_set_data (backing_store->mask_texture, COGL_PIXEL_FORMAT_A_8,
776610
+                              cairo_image_surface_get_stride (backing_store->mask_surface),
776610
+                              cairo_image_surface_get_data (backing_store->mask_surface), 0,
776610
+                              &error))
776610
+    {
776610
+
776610
+      g_warning ("Failed to update backing mask texture");
776610
+      g_clear_pointer (&error, cogl_error_free);
776610
+    }
776610
+}
776610
+
776610
+static gboolean
776610
+meta_texture_backing_store_shrink (MetaTextureBackingStore     *backing_store,
776610
+                                   const cairo_rectangle_int_t *area)
776610
+{
776610
+  cairo_t *cr;
776610
+
776610
+  cairo_region_subtract_rectangle (backing_store->region, area);
776610
+
776610
+  /* If the client has finally redrawn the entire surface, we can
776610
+   * ditch our snapshot
776610
+   */
776610
+  if (cairo_region_is_empty (backing_store->region))
776610
+    return FALSE;
776610
+
776610
+  cr = cairo_create (backing_store->mask_surface);
776610
+  cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
776610
+  cairo_paint (cr);
776610
+  gdk_cairo_region (cr, backing_store->region);
776610
+  cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR);
776610
+  cairo_fill (cr);
776610
+  cairo_destroy (cr);
776610
+
776610
+  meta_texture_backing_store_redraw_mask (backing_store);
776610
+
776610
+  return TRUE;
776610
+}
776610
+
776610
+static void
776610
+shrink_backing_region (MetaShapedTexture           *stex,
776610
+                       const cairo_rectangle_int_t *area)
776610
+{
776610
+  MetaShapedTexturePrivate *priv = stex->priv;
776610
+  gboolean still_backing_texture;
776610
+
776610
+  if (!priv->backing_store)
776610
+    return;
776610
+
776610
+  still_backing_texture =
776610
+      meta_texture_backing_store_shrink (priv->backing_store, area);
776610
+
776610
+  if (!still_backing_texture)
776610
+    disable_backing_store (stex);
776610
+}
776610
+
776610
 /**
776610
  * meta_shaped_texture_update_area:
776610
  * @stex: #MetaShapedTexture
776610
  * @x: the x coordinate of the damaged area
776610
  * @y: the y coordinate of the damaged area
776610
  * @width: the width of the damaged area
776610
  * @height: the height of the damaged area
776610
  *
776610
  * Repairs the damaged area indicated by @x, @y, @width and @height
776610
  * and potentially queues a redraw.
776610
  *
776610
  * Return value: Whether a redraw have been queued or not
776610
  */
776610
 gboolean
776610
 meta_shaped_texture_update_area (MetaShapedTexture *stex,
776610
 				 int                x,
776610
 				 int                y,
776610
 				 int                width,
776610
 				 int                height)
776610
 {
776610
   MetaShapedTexturePrivate *priv;
776610
   cairo_region_t *unobscured_region;
776610
   const cairo_rectangle_int_t clip = { x, y, width, height };
776610
 
776610
   priv = stex->priv;
776610
 
776610
   if (priv->texture == NULL)
776610
     return FALSE;
776610
 
776610
+  shrink_backing_region (stex, &clip);
776610
+
776610
   meta_texture_tower_update_area (priv->paint_tower, x, y, width, height);
776610
 
776610
   unobscured_region = effective_unobscured_region (stex);
776610
   if (unobscured_region)
776610
     {
776610
       cairo_region_t *intersection;
776610
 
776610
       if (cairo_region_is_empty (unobscured_region))
776610
         return FALSE;
776610
 
776610
       intersection = cairo_region_copy (unobscured_region);
776610
       cairo_region_intersect_rectangle (intersection, &clip);
776610
 
776610
       if (!cairo_region_is_empty (intersection))
776610
         {
776610
           cairo_rectangle_int_t damage_rect;
776610
           cairo_region_get_extents (intersection, &damage_rect);
776610
           clutter_actor_queue_redraw_with_clip (CLUTTER_ACTOR (stex), &damage_rect);
776610
           cairo_region_destroy (intersection);
776610
           return TRUE;
776610
         }
776610
 
776610
       cairo_region_destroy (intersection);
776610
       return FALSE;
776610
     }
776610
   else
776610
     {
776610
       clutter_actor_queue_redraw_with_clip (CLUTTER_ACTOR (stex), &clip);
776610
       return TRUE;
776610
     }
776610
@@ -892,62 +1103,63 @@ meta_shaped_texture_set_opaque_region (MetaShapedTexture *stex,
776610
   priv = stex->priv;
776610
 
776610
   if (priv->opaque_region)
776610
     cairo_region_destroy (priv->opaque_region);
776610
 
776610
   if (opaque_region)
776610
     priv->opaque_region = cairo_region_reference (opaque_region);
776610
   else
776610
     priv->opaque_region = NULL;
776610
 }
776610
 
776610
 cairo_region_t *
776610
 meta_shaped_texture_get_opaque_region (MetaShapedTexture *stex)
776610
 {
776610
   MetaShapedTexturePrivate *priv = stex->priv;
776610
   return priv->opaque_region;
776610
 }
776610
 
776610
 static gboolean
776610
 should_get_via_offscreen (MetaShapedTexture *stex)
776610
 {
776610
   MetaShapedTexturePrivate *priv = stex->priv;
776610
 
776610
   if (!cogl_texture_is_get_data_supported (priv->texture))
776610
     return TRUE;
776610
 
776610
   return FALSE;
776610
 }
776610
 
776610
 static cairo_surface_t *
776610
-get_image_via_offscreen (MetaShapedTexture     *stex,
776610
-                         cairo_rectangle_int_t *clip)
776610
+get_image_via_offscreen (MetaShapedTexture      *stex,
776610
+                         cairo_rectangle_int_t  *clip,
776610
+                         CoglTexture           **texture)
776610
 {
776610
   MetaShapedTexturePrivate *priv = stex->priv;
776610
   ClutterBackend *clutter_backend = clutter_get_default_backend ();
776610
   CoglContext *cogl_context =
776610
     clutter_backend_get_cogl_context (clutter_backend);
776610
   CoglTexture *image_texture;
776610
   GError *error = NULL;
776610
   CoglOffscreen *offscreen;
776610
   CoglFramebuffer *fb;
776610
   CoglMatrix projection_matrix;
776610
   unsigned int fb_width, fb_height;
776610
   cairo_rectangle_int_t fallback_clip;
776610
   CoglColor clear_color;
776610
   cairo_surface_t *surface;
776610
 
776610
   if (cogl_has_feature (cogl_context, COGL_FEATURE_ID_TEXTURE_NPOT))
776610
     {
776610
       fb_width = priv->tex_width;
776610
       fb_height = priv->tex_height;
776610
     }
776610
   else
776610
     {
776610
       fb_width = clutter_util_next_p2 (priv->tex_width);
776610
       fb_height = clutter_util_next_p2 (priv->tex_height);
776610
     }
776610
 
776610
   if (!clip)
776610
     {
776610
       fallback_clip = (cairo_rectangle_int_t) {
776610
         .width = priv->tex_width,
776610
@@ -988,184 +1200,424 @@ get_image_via_offscreen (MetaShapedTexture     *stex,
776610
       g_error_free (error);
776610
       cogl_object_unref (fb);
776610
       return FALSE;
776610
     }
776610
 
776610
   cogl_framebuffer_push_matrix (fb);
776610
   cogl_matrix_init_identity (&projection_matrix);
776610
   cogl_matrix_scale (&projection_matrix,
776610
                      1.0 / (priv->tex_width / 2.0),
776610
                      -1.0 / (priv->tex_height / 2.0), 0);
776610
   cogl_matrix_translate (&projection_matrix,
776610
                          -(priv->tex_width / 2.0),
776610
                          -(priv->tex_height / 2.0), 0);
776610
 
776610
   cogl_framebuffer_set_projection_matrix (fb, &projection_matrix);
776610
 
776610
   cogl_color_init_from_4ub (&clear_color, 0, 0, 0, 0);
776610
   cogl_framebuffer_clear (fb, COGL_BUFFER_BIT_COLOR, &clear_color);
776610
 
776610
   do_paint (stex, fb, priv->texture, NULL);
776610
 
776610
   cogl_framebuffer_pop_matrix (fb);
776610
 
776610
   surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
776610
                                         clip->width, clip->height);
776610
   cogl_framebuffer_read_pixels (fb,
776610
                                 clip->x, clip->y,
776610
                                 clip->width, clip->height,
776610
                                 CLUTTER_CAIRO_FORMAT_ARGB32,
776610
                                 cairo_image_surface_get_data (surface));
776610
+  cairo_surface_mark_dirty (surface);
776610
+
776610
+  if (texture)
776610
+    {
776610
+      *texture = cogl_object_ref (image_texture);
776610
+
776610
+      if (G_UNLIKELY (meta_debug_show_backing_store))
776610
+        {
776610
+          cairo_t *cr;
776610
+
776610
+          cr = cairo_create (surface);
776610
+          cairo_set_source_rgba (cr, 1.0, 0.0, 0.0, 0.75);
776610
+          cairo_paint (cr);
776610
+          cairo_destroy (cr);
776610
+        }
776610
+
776610
+      cogl_texture_set_data (*texture, CLUTTER_CAIRO_FORMAT_ARGB32,
776610
+                             cairo_image_surface_get_stride (surface),
776610
+                             cairo_image_surface_get_data (surface), 0, NULL);
776610
+    }
776610
+
776610
   cogl_object_unref (fb);
776610
 
776610
-  cairo_surface_mark_dirty (surface);
776610
 
776610
   return surface;
776610
 }
776610
 
776610
 /**
776610
  * meta_shaped_texture_get_image:
776610
  * @stex: A #MetaShapedTexture
776610
  * @clip: A clipping rectangle, to help prevent extra processing.
776610
  * In the case that the clipping rectangle is partially or fully
776610
  * outside the bounds of the texture, the rectangle will be clipped.
776610
  *
776610
  * Flattens the two layers of the shaped texture into one ARGB32
776610
  * image by alpha blending the two images, and returns the flattened
776610
  * image.
776610
  *
776610
  * Returns: (transfer full): a new cairo surface to be freed with
776610
  * cairo_surface_destroy().
776610
  */
776610
 cairo_surface_t *
776610
 meta_shaped_texture_get_image (MetaShapedTexture     *stex,
776610
                                cairo_rectangle_int_t *clip)
776610
 {
776610
   MetaShapedTexturePrivate *priv = stex->priv;
776610
   cairo_rectangle_int_t *transformed_clip = NULL;
776610
   CoglTexture *texture, *mask_texture;
776610
   cairo_surface_t *surface;
776610
 
776610
   g_return_val_if_fail (META_IS_SHAPED_TEXTURE (stex), NULL);
776610
 
776610
   texture = COGL_TEXTURE (priv->texture);
776610
 
776610
   if (texture == NULL)
776610
     return NULL;
776610
 
776610
   if (priv->tex_width == 0 || priv->tex_height == 0)
776610
     return NULL;
776610
 
776610
   if (clip != NULL)
776610
     {
776610
       double tex_scale;
776610
       cairo_rectangle_int_t tex_rect;
776610
 
776610
       transformed_clip = alloca (sizeof (cairo_rectangle_int_t));
776610
       *transformed_clip = *clip;
776610
 
776610
       clutter_actor_get_scale (CLUTTER_ACTOR (stex), &tex_scale, NULL);
776610
       meta_rectangle_scale_double (transformed_clip, 1.0 / tex_scale,
776610
                                    META_ROUNDING_STRATEGY_GROW);
776610
 
776610
       tex_rect = (cairo_rectangle_int_t) {
776610
         .width = priv->tex_width,
776610
         .height = priv->tex_height,
776610
       };
776610
 
776610
       if (!meta_rectangle_intersect (&tex_rect, transformed_clip,
776610
                                      transformed_clip))
776610
         return NULL;
776610
     }
776610
 
776610
   if (should_get_via_offscreen (stex))
776610
-    return get_image_via_offscreen (stex, transformed_clip);
776610
+    return get_image_via_offscreen (stex, transformed_clip, NULL);
776610
 
776610
   if (transformed_clip)
776610
     texture = cogl_texture_new_from_sub_texture (texture,
776610
                                                  transformed_clip->x,
776610
                                                  transformed_clip->y,
776610
                                                  transformed_clip->width,
776610
                                                  transformed_clip->height);
776610
 
776610
   surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
776610
                                         cogl_texture_get_width (texture),
776610
                                         cogl_texture_get_height (texture));
776610
 
776610
   cogl_texture_get_data (texture, CLUTTER_CAIRO_FORMAT_ARGB32,
776610
                          cairo_image_surface_get_stride (surface),
776610
                          cairo_image_surface_get_data (surface));
776610
 
776610
   cairo_surface_mark_dirty (surface);
776610
 
776610
   if (transformed_clip)
776610
     cogl_object_unref (texture);
776610
 
776610
   mask_texture = priv->mask_texture;
776610
   if (mask_texture != NULL)
776610
     {
776610
       cairo_t *cr;
776610
       cairo_surface_t *mask_surface;
776610
 
776610
       if (transformed_clip)
776610
         mask_texture =
776610
           cogl_texture_new_from_sub_texture (mask_texture,
776610
                                              transformed_clip->x,
776610
                                              transformed_clip->y,
776610
                                              transformed_clip->width,
776610
                                              transformed_clip->height);
776610
 
776610
       mask_surface = cairo_image_surface_create (CAIRO_FORMAT_A8,
776610
                                                  cogl_texture_get_width (mask_texture),
776610
                                                  cogl_texture_get_height (mask_texture));
776610
 
776610
       cogl_texture_get_data (mask_texture, COGL_PIXEL_FORMAT_A_8,
776610
                              cairo_image_surface_get_stride (mask_surface),
776610
                              cairo_image_surface_get_data (mask_surface));
776610
 
776610
       cairo_surface_mark_dirty (mask_surface);
776610
 
776610
       cr = cairo_create (surface);
776610
       cairo_set_source_surface (cr, mask_surface, 0, 0);
776610
       cairo_set_operator (cr, CAIRO_OPERATOR_DEST_IN);
776610
       cairo_paint (cr);
776610
       cairo_destroy (cr);
776610
 
776610
       cairo_surface_destroy (mask_surface);
776610
 
776610
       if (transformed_clip)
776610
         cogl_object_unref (mask_texture);
776610
     }
776610
 
776610
   return surface;
776610
 }
776610
 
776610
+static void
776610
+meta_texture_backing_store_free (MetaTextureBackingStore *backing_store)
776610
+{
776610
+  g_clear_pointer (&backing_store->texture, cogl_object_unref);
776610
+  g_clear_pointer (&backing_store->mask_texture, cogl_object_unref);
776610
+  g_clear_pointer (&backing_store->mask_surface, cairo_surface_destroy);
776610
+  g_clear_pointer (&backing_store->region, cairo_region_destroy);
776610
+
776610
+  g_slice_free (MetaTextureBackingStore, backing_store);
776610
+}
776610
+
776610
+static MetaTextureBackingStore *
776610
+meta_texture_backing_store_new (CoglTexture *texture)
776610
+{
776610
+  MetaTextureBackingStore *backing_store = NULL;
776610
+  ClutterBackend *backend = clutter_get_default_backend ();
776610
+  CoglContext *context = clutter_backend_get_cogl_context (backend);
776610
+  CoglTexture *mask_texture = NULL;
776610
+  guchar *mask_data;
776610
+  int width, height, stride;
776610
+  cairo_surface_t *surface;
776610
+  cairo_region_t *region;
776610
+  cairo_rectangle_int_t backing_rectangle;
776610
+
776610
+  width = cogl_texture_get_width (texture);
776610
+  height = cogl_texture_get_height (texture);
776610
+  stride = cairo_format_stride_for_width (CAIRO_FORMAT_A8, width);
776610
+
776610
+  /* we start off by only letting the backing texture through, and none of the real texture */
776610
+  backing_rectangle.x = 0;
776610
+  backing_rectangle.y = 0;
776610
+  backing_rectangle.width = width;
776610
+  backing_rectangle.height = height;
776610
+
776610
+  region = cairo_region_create_rectangle (&backing_rectangle);
776610
+
776610
+  /* initialize mask to transparent, so the entire backing store shows through
776610
+   * up front
776610
+   */
776610
+  mask_data = g_malloc0 (stride * height);
776610
+  surface = cairo_image_surface_create_for_data (mask_data,
776610
+                                                 CAIRO_FORMAT_A8,
776610
+                                                 width,
776610
+                                                 height,
776610
+                                                 stride);
776610
+
776610
+  if (meta_texture_rectangle_check (texture))
776610
+    {
776610
+      mask_texture = COGL_TEXTURE (cogl_texture_rectangle_new_with_size (context,
776610
+                                                                         width,
776610
+                                                                         height));
776610
+      cogl_texture_set_components (mask_texture, COGL_TEXTURE_COMPONENTS_A);
776610
+      cogl_texture_set_region (mask_texture,
776610
+                               0, 0,
776610
+                               0, 0,
776610
+                               width, height,
776610
+                               width, height,
776610
+                               COGL_PIXEL_FORMAT_A_8,
776610
+                               stride, mask_data);
776610
+    }
776610
+  else
776610
+    {
776610
+      CoglError *error = NULL;
776610
+
776610
+      mask_texture = COGL_TEXTURE (cogl_texture_2d_new_from_data (context, width, height,
776610
+                                                                  COGL_PIXEL_FORMAT_A_8,
776610
+                                                                  stride, mask_data, &error));
776610
+
776610
+      if (error)
776610
+        {
776610
+          g_warning ("Failed to allocate mask texture: %s", error->message);
776610
+          cogl_error_free (error);
776610
+        }
776610
+    }
776610
+
776610
+  if (mask_texture)
776610
+    {
776610
+      backing_store = g_slice_new0 (MetaTextureBackingStore);
776610
+      backing_store->texture = cogl_object_ref (texture);
776610
+      backing_store->mask_texture = mask_texture;
776610
+      backing_store->mask_surface = surface;
776610
+      backing_store->region = region;
776610
+    }
776610
+
776610
+  return backing_store;
776610
+}
776610
+
776610
+static void
776610
+enable_backing_store (MetaShapedTexture *stex,
776610
+                      CoglTexture       *texture)
776610
+{
776610
+  MetaShapedTexturePrivate *priv = stex->priv;
776610
+
776610
+  g_clear_pointer (&priv->backing_store, meta_texture_backing_store_free);
776610
+
776610
+  priv->backing_store = meta_texture_backing_store_new (texture);
776610
+
776610
+  meta_shaped_texture_reset_pipelines (stex);
776610
+}
776610
+
776610
+static void
776610
+disable_backing_store (MetaShapedTexture *stex)
776610
+{
776610
+  MetaShapedTexturePrivate *priv = stex->priv;
776610
+
776610
+  g_clear_pointer (&priv->backing_store, meta_texture_backing_store_free);
776610
+
776610
+  meta_shaped_texture_reset_pipelines (stex);
776610
+}
776610
+
776610
+void
776610
+meta_shaped_texture_save (MetaShapedTexture *stex)
776610
+{
776610
+
776610
+  CoglTexture *texture, *mask_texture;
776610
+  MetaShapedTexturePrivate *priv = stex->priv;
776610
+  cairo_surface_t *surface;
776610
+
776610
+  g_return_if_fail (META_IS_SHAPED_TEXTURE (stex));
776610
+
776610
+  texture = COGL_TEXTURE (priv->texture);
776610
+
776610
+  if (texture == NULL)
776610
+    return;
776610
+
776610
+  g_clear_pointer (&priv->saved_base_surface, cairo_surface_destroy);
776610
+  g_clear_pointer (&priv->saved_mask_surface, cairo_surface_destroy);
776610
+  g_clear_pointer (&priv->backing_store, meta_texture_backing_store_free);
776610
+
776610
+  if (should_get_via_offscreen (stex))
776610
+    {
776610
+      CoglTexture *backing_texture;
776610
+
776610
+      meta_shaped_texture_reset_pipelines (stex);
776610
+
776610
+      surface = get_image_via_offscreen (stex, NULL, &backing_texture);
776610
+
776610
+      enable_backing_store (stex, backing_texture);
776610
+      cogl_object_unref (backing_texture);
776610
+    }
776610
+  else
776610
+    {
776610
+      surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
776610
+                                            cogl_texture_get_width (texture),
776610
+                                            cogl_texture_get_height (texture));
776610
+
776610
+      cogl_texture_get_data (texture, CLUTTER_CAIRO_FORMAT_ARGB32,
776610
+                             cairo_image_surface_get_stride (surface),
776610
+                             cairo_image_surface_get_data (surface));
776610
+    }
776610
+
776610
+  priv->saved_base_surface = surface;
776610
+
776610
+  mask_texture = stex->priv->mask_texture;
776610
+  if (mask_texture != NULL)
776610
+    {
776610
+      cairo_surface_t *mask_surface;
776610
+
776610
+      mask_surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
776610
+                                                 cogl_texture_get_width (mask_texture),
776610
+                                                 cogl_texture_get_height (mask_texture));
776610
+
776610
+      cogl_texture_get_data (mask_texture, CLUTTER_CAIRO_FORMAT_ARGB32,
776610
+                             cairo_image_surface_get_stride (mask_surface),
776610
+                             cairo_image_surface_get_data (mask_surface));
776610
+
776610
+      cairo_surface_mark_dirty (mask_surface);
776610
+
776610
+      priv->saved_mask_surface = mask_surface;
776610
+    }
776610
+}
776610
+
776610
+void
776610
+meta_shaped_texture_restore (MetaShapedTexture *stex)
776610
+{
776610
+  MetaShapedTexturePrivate *priv = stex->priv;
776610
+  CoglTexture *texture;
776610
+  CoglError *error = NULL;
776610
+
776610
+  texture = meta_shaped_texture_get_texture (stex);
776610
+
776610
+  if (texture == NULL)
776610
+    return;
776610
+
776610
+  if (priv->mask_texture)
776610
+    {
776610
+      if (!cogl_texture_set_data (priv->mask_texture, CLUTTER_CAIRO_FORMAT_ARGB32,
776610
+                                  cairo_image_surface_get_stride (priv->saved_mask_surface),
776610
+                                  cairo_image_surface_get_data (priv->saved_mask_surface), 0,
776610
+                                  &error))
776610
+        {
776610
+          g_warning ("Failed to restore mask texture");
776610
+          g_clear_pointer (&error, cogl_error_free);
776610
+        }
776610
+      g_clear_pointer (&priv->saved_mask_surface, cairo_surface_destroy);
776610
+    }
776610
+
776610
+  /* if the main texture doesn't support direct writes, then
776610
+   * write to the local backing texture instead, and blend old
776610
+   * versus new at paint time.
776610
+   */
776610
+  if (priv->backing_store)
776610
+    {
776610
+      meta_texture_backing_store_redraw_mask (priv->backing_store);
776610
+      texture = priv->backing_store->texture;
776610
+    }
776610
+
776610
+  if (!cogl_texture_set_data (texture, CLUTTER_CAIRO_FORMAT_ARGB32,
776610
+                              cairo_image_surface_get_stride (priv->saved_base_surface),
776610
+                              cairo_image_surface_get_data (priv->saved_base_surface), 0,
776610
+                              &error))
776610
+    {
776610
+      g_warning ("Failed to restore texture");
776610
+      g_clear_pointer (&error, cogl_error_free);
776610
+    }
776610
+  g_clear_pointer (&priv->saved_base_surface, cairo_surface_destroy);
776610
+
776610
+  clutter_actor_queue_redraw (CLUTTER_ACTOR (stex));
776610
+}
776610
+
776610
 void
776610
 meta_shaped_texture_set_fallback_size (MetaShapedTexture *self,
776610
                                        guint              fallback_width,
776610
                                        guint              fallback_height)
776610
 {
776610
   MetaShapedTexturePrivate *priv = self->priv;
776610
 
776610
   priv->fallback_width = fallback_width;
776610
   priv->fallback_height = fallback_height;
776610
 }
776610
 
776610
 static void
776610
 meta_shaped_texture_cull_out (MetaCullable   *cullable,
776610
                               cairo_region_t *unobscured_region,
776610
                               cairo_region_t *clip_region)
776610
 {
776610
   MetaShapedTexture *self = META_SHAPED_TEXTURE (cullable);
776610
   MetaShapedTexturePrivate *priv = self->priv;
776610
 
776610
   set_unobscured_region (self, unobscured_region);
776610
   set_clip_region (self, clip_region);
776610
 
776610
   if (clutter_actor_get_paint_opacity (CLUTTER_ACTOR (self)) == 0xff)
776610
     {
776610
       if (priv->opaque_region)
776610
         {
776610
           if (unobscured_region)
776610
             cairo_region_subtract (unobscured_region, priv->opaque_region);
776610
           if (clip_region)
776610
             cairo_region_subtract (clip_region, priv->opaque_region);
776610
diff --git a/src/meta/meta-shaped-texture.h b/src/meta/meta-shaped-texture.h
776610
index 80b23f2ea..fc0567c9f 100644
776610
--- a/src/meta/meta-shaped-texture.h
776610
+++ b/src/meta/meta-shaped-texture.h
776610
@@ -54,33 +54,35 @@ struct _MetaShapedTextureClass
776610
  */
776610
 struct _MetaShapedTexture
776610
 {
776610
   /*< private >*/
776610
   ClutterActor parent;
776610
 
776610
   MetaShapedTexturePrivate *priv;
776610
 };
776610
 
776610
 GType meta_shaped_texture_get_type (void) G_GNUC_CONST;
776610
 
776610
 void meta_shaped_texture_set_create_mipmaps (MetaShapedTexture *stex,
776610
 					     gboolean           create_mipmaps);
776610
 
776610
 gboolean meta_shaped_texture_update_area (MetaShapedTexture *stex,
776610
                                           int                x,
776610
                                           int                y,
776610
                                           int                width,
776610
                                           int                height);
776610
 
776610
 CoglTexture * meta_shaped_texture_get_texture (MetaShapedTexture *stex);
776610
 
776610
 void meta_shaped_texture_set_mask_texture (MetaShapedTexture *stex,
776610
                                            CoglTexture       *mask_texture);
776610
 void meta_shaped_texture_set_opaque_region (MetaShapedTexture *stex,
776610
                                             cairo_region_t    *opaque_region);
776610
 
776610
 cairo_surface_t * meta_shaped_texture_get_image (MetaShapedTexture     *stex,
776610
                                                  cairo_rectangle_int_t *clip);
776610
 
776610
+void meta_shaped_texture_save (MetaShapedTexture *self);
776610
+void meta_shaped_texture_restore (MetaShapedTexture *self);
776610
 G_END_DECLS
776610
 
776610
 #endif /* __META_SHAPED_TEXTURE_H__ */
776610
-- 
776610
2.18.1
776610