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

657d8e
From d8cc418899276b45cb1a787493e0998e3b008fe5 Mon Sep 17 00:00:00 2001
657d8e
From: Ray Strode <rstrode@redhat.com>
657d8e
Date: Thu, 10 Jan 2019 10:48:02 -0500
657d8e
Subject: [PATCH 9/9] MetaShapedTexture: save and restore textures on suspend
657d8e
657d8e
The proprietary nvidia driver garbles GPU memory on suspend.
657d8e
657d8e
In order to workaround that limitation, this commit copies all
657d8e
textures to host memory on suspend and restores them on resume.
657d8e
657d8e
One complication comes from external textures (such as those
657d8e
given to us by Xwayland for X clients).  We can't just restore
657d8e
those textures, since they aren't writable.
657d8e
657d8e
This commit addresses that complication by keeping a local texture
657d8e
around for those external textures, and using it instead for parts
657d8e
of the window that haven't been redrawn since resume.
657d8e
---
657d8e
 src/compositor/meta-shaped-texture.c | 487 +++++++++++++++++++++++++--
657d8e
 src/meta/meta-shaped-texture.h       |   2 +
657d8e
 2 files changed, 468 insertions(+), 21 deletions(-)
657d8e
657d8e
diff --git a/src/compositor/meta-shaped-texture.c b/src/compositor/meta-shaped-texture.c
657d8e
index d64e214e5..ea8daa03d 100644
657d8e
--- a/src/compositor/meta-shaped-texture.c
657d8e
+++ b/src/compositor/meta-shaped-texture.c
657d8e
@@ -40,7 +40,9 @@
657d8e
 #include "compositor/meta-texture-tower.h"
657d8e
 #include "compositor/region-utils.h"
657d8e
 #include "core/boxes-private.h"
657d8e
+#include <meta/meta-backend.h>
657d8e
 #include "meta/meta-shaped-texture.h"
657d8e
+#include "meta-texture-rectangle.h"
657d8e
 
657d8e
 /* MAX_MIPMAPPING_FPS needs to be as small as possible for the best GPU
657d8e
  * performance, but higher than the refresh rate of commonly slow updating
657d8e
@@ -72,8 +74,12 @@ static void meta_shaped_texture_get_preferred_height (ClutterActor *self,
657d8e
 
657d8e
 static gboolean meta_shaped_texture_get_paint_volume (ClutterActor *self, ClutterPaintVolume *volume);
657d8e
 
657d8e
+static void disable_backing_store (MetaShapedTexture *stex);
657d8e
+
657d8e
 static void cullable_iface_init (MetaCullableInterface *iface);
657d8e
 
657d8e
+static gboolean meta_debug_show_backing_store = FALSE;
657d8e
+
657d8e
 enum
657d8e
 {
657d8e
   SIZE_CHANGED,
657d8e
@@ -83,6 +89,14 @@ enum
657d8e
 
657d8e
 static guint signals[LAST_SIGNAL];
657d8e
 
657d8e
+typedef struct
657d8e
+{
657d8e
+  CoglTexture *texture;
657d8e
+  CoglTexture *mask_texture;
657d8e
+  cairo_surface_t *mask_surface;
657d8e
+  cairo_region_t *region;
657d8e
+} MetaTextureBackingStore;
657d8e
+
657d8e
 struct _MetaShapedTexture
657d8e
 {
657d8e
   ClutterActor parent;
657d8e
@@ -114,6 +128,16 @@ struct _MetaShapedTexture
657d8e
   int viewport_dst_width;
657d8e
   int viewport_dst_height;
657d8e
 
657d8e
+  /* textures get corrupted on suspend, so save them */
657d8e
+  cairo_surface_t *saved_base_surface;
657d8e
+  cairo_surface_t *saved_mask_surface;
657d8e
+
657d8e
+  /* We can't just restore external textures, so we need to track
657d8e
+   * which parts of the external texture are freshly drawn from
657d8e
+   * the client after corruption, and fill in the rest from our
657d8e
+   * saved snapshot */
657d8e
+  MetaTextureBackingStore *backing_store;
657d8e
+
657d8e
   int tex_width, tex_height;
657d8e
   int fallback_width, fallback_height;
657d8e
   int dst_width, dst_height;
657d8e
@@ -148,6 +172,9 @@ meta_shaped_texture_class_init (MetaShapedTextureClass *klass)
657d8e
                                         0,
657d8e
                                         NULL, NULL, NULL,
657d8e
                                         G_TYPE_NONE, 0);
657d8e
+
657d8e
+  if (g_getenv ("MUTTER_DEBUG_BACKING_STORE"))
657d8e
+    meta_debug_show_backing_store = TRUE;
657d8e
 }
657d8e
 
657d8e
 static void
657d8e
@@ -159,6 +186,11 @@ invalidate_size (MetaShapedTexture *stex)
657d8e
 static void
657d8e
 meta_shaped_texture_init (MetaShapedTexture *stex)
657d8e
 {
657d8e
+  MetaBackend *backend = meta_get_backend ();
657d8e
+  ClutterBackend *clutter_backend = clutter_get_default_backend ();
657d8e
+  CoglContext *cogl_context =
657d8e
+    clutter_backend_get_cogl_context (clutter_backend);
657d8e
+
657d8e
   stex->paint_tower = meta_texture_tower_new ();
657d8e
 
657d8e
   stex->texture = NULL;
657d8e
@@ -171,6 +203,12 @@ meta_shaped_texture_init (MetaShapedTexture *stex)
657d8e
                     "notify::scale-x",
657d8e
                     G_CALLBACK (invalidate_size),
657d8e
                     stex);
657d8e
+
657d8e
+  if (cogl_has_feature (cogl_context, COGL_FEATURE_ID_UNSTABLE_TEXTURES))
657d8e
+    {
657d8e
+      g_signal_connect_object (backend, "suspending", G_CALLBACK (meta_shaped_texture_save), stex, G_CONNECT_SWAPPED);
657d8e
+      g_signal_connect_object (backend, "resuming", G_CALLBACK (meta_shaped_texture_restore), stex, G_CONNECT_SWAPPED);
657d8e
+    }
657d8e
 }
657d8e
 
657d8e
 static void
657d8e
@@ -311,24 +349,72 @@ meta_shaped_texture_dispose (GObject *object)
657d8e
   G_OBJECT_CLASS (meta_shaped_texture_parent_class)->dispose (object);
657d8e
 }
657d8e
 
657d8e
+static int
657d8e
+get_layer_indices (MetaShapedTexture *stex,
657d8e
+                   int               *main_layer_index,
657d8e
+                   int               *backing_mask_layer_index,
657d8e
+                   int               *backing_layer_index,
657d8e
+                   int               *mask_layer_index)
657d8e
+{
657d8e
+  int next_layer_index = 0;
657d8e
+
657d8e
+  if (main_layer_index)
657d8e
+    *main_layer_index = next_layer_index;
657d8e
+
657d8e
+  next_layer_index++;
657d8e
+
657d8e
+  if (stex->backing_store)
657d8e
+    {
657d8e
+      if (backing_mask_layer_index)
657d8e
+        *backing_mask_layer_index = next_layer_index;
657d8e
+      next_layer_index++;
657d8e
+      if (backing_layer_index)
657d8e
+        *backing_layer_index = next_layer_index;
657d8e
+      next_layer_index++;
657d8e
+    }
657d8e
+  else
657d8e
+    {
657d8e
+      if (backing_mask_layer_index)
657d8e
+        *backing_mask_layer_index = -1;
657d8e
+      if (backing_layer_index)
657d8e
+        *backing_layer_index = -1;
657d8e
+    }
657d8e
+
657d8e
+  if (mask_layer_index)
657d8e
+    *mask_layer_index = next_layer_index;
657d8e
+
657d8e
+  return next_layer_index;
657d8e
+}
657d8e
+
657d8e
 static CoglPipeline *
657d8e
 get_base_pipeline (MetaShapedTexture *stex,
657d8e
                    CoglContext       *ctx)
657d8e
 {
657d8e
   CoglPipeline *pipeline;
657d8e
+  int main_layer_index;
657d8e
+  int backing_layer_index;
657d8e
+  int backing_mask_layer_index;
657d8e
+  int i, number_of_layers;
657d8e
 
657d8e
   if (stex->base_pipeline)
657d8e
     return stex->base_pipeline;
657d8e
 
657d8e
   pipeline = cogl_pipeline_new (ctx);
657d8e
-  cogl_pipeline_set_layer_wrap_mode_s (pipeline, 0,
657d8e
-                                       COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE);
657d8e
-  cogl_pipeline_set_layer_wrap_mode_t (pipeline, 0,
657d8e
-                                       COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE);
657d8e
-  cogl_pipeline_set_layer_wrap_mode_s (pipeline, 1,
657d8e
-                                       COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE);
657d8e
-  cogl_pipeline_set_layer_wrap_mode_t (pipeline, 1,
657d8e
-                                       COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE);
657d8e
+
657d8e
+  number_of_layers = get_layer_indices (stex,
657d8e
+                                        &main_layer_index,
657d8e
+                                        &backing_mask_layer_index,
657d8e
+                                        &backing_layer_index,
657d8e
+                                        NULL);
657d8e
+
657d8e
+  for (i = 0; i < number_of_layers; i++)
657d8e
+    {
657d8e
+      cogl_pipeline_set_layer_wrap_mode_s (pipeline, i,
657d8e
+                                           COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE);
657d8e
+      cogl_pipeline_set_layer_wrap_mode_t (pipeline, i,
657d8e
+                                           COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE);
657d8e
+    }
657d8e
+
657d8e
   if (!stex->is_y_inverted)
657d8e
     {
657d8e
       CoglMatrix matrix;
657d8e
@@ -336,7 +422,22 @@ get_base_pipeline (MetaShapedTexture *stex,
657d8e
       cogl_matrix_init_identity (&matrix);
657d8e
       cogl_matrix_scale (&matrix, 1, -1, 1);
657d8e
       cogl_matrix_translate (&matrix, 0, -1, 0);
657d8e
-      cogl_pipeline_set_layer_matrix (pipeline, 0, &matrix);
657d8e
+      cogl_pipeline_set_layer_matrix (pipeline, main_layer_index, &matrix);
657d8e
+    }
657d8e
+
657d8e
+  if (stex->backing_store)
657d8e
+    {
657d8e
+      g_autofree char *backing_description = NULL;
657d8e
+      cogl_pipeline_set_layer_combine (pipeline, backing_mask_layer_index,
657d8e
+                                       "RGBA = REPLACE(PREVIOUS)",
657d8e
+                                       NULL);
657d8e
+      backing_description = g_strdup_printf ("RGBA = INTERPOLATE(PREVIOUS, TEXTURE_%d, TEXTURE_%d[A])",
657d8e
+                                             backing_layer_index,
657d8e
+                                             backing_mask_layer_index);
657d8e
+      cogl_pipeline_set_layer_combine (pipeline,
657d8e
+                                       backing_layer_index,
657d8e
+                                       backing_description,
657d8e
+                                       NULL);
657d8e
     }
657d8e
 
657d8e
   if (stex->transform != META_MONITOR_TRANSFORM_NORMAL)
657d8e
@@ -379,7 +480,7 @@ get_base_pipeline (MetaShapedTexture *stex,
657d8e
     }
657d8e
 
657d8e
   if (stex->snippet)
657d8e
-    cogl_pipeline_add_layer_snippet (pipeline, 0, stex->snippet);
657d8e
+    cogl_pipeline_add_layer_snippet (pipeline, main_layer_index, stex->snippet);
657d8e
 
657d8e
   stex->base_pipeline = pipeline;
657d8e
 
657d8e
@@ -398,12 +499,15 @@ get_masked_pipeline (MetaShapedTexture *stex,
657d8e
                      CoglContext       *ctx)
657d8e
 {
657d8e
   CoglPipeline *pipeline;
657d8e
+  int mask_layer_index;
657d8e
 
657d8e
   if (stex->masked_pipeline)
657d8e
     return stex->masked_pipeline;
657d8e
 
657d8e
+  get_layer_indices (stex, NULL, NULL, NULL, &mask_layer_index);
657d8e
+
657d8e
   pipeline = cogl_pipeline_copy (get_base_pipeline (stex, ctx));
657d8e
-  cogl_pipeline_set_layer_combine (pipeline, 1,
657d8e
+  cogl_pipeline_set_layer_combine (pipeline, mask_layer_index,
657d8e
                                    "RGBA = MODULATE (PREVIOUS, TEXTURE[A])",
657d8e
                                    NULL);
657d8e
 
657d8e
@@ -517,6 +621,8 @@ set_cogl_texture (MetaShapedTexture *stex,
657d8e
   if (stex->texture)
657d8e
     cogl_object_unref (stex->texture);
657d8e
 
657d8e
+  g_clear_pointer (&stex->saved_base_surface, cairo_surface_destroy);
657d8e
+
657d8e
   stex->texture = cogl_tex;
657d8e
 
657d8e
   if (cogl_tex != NULL)
657d8e
@@ -579,6 +685,10 @@ do_paint (MetaShapedTexture *stex,
657d8e
   CoglContext *ctx;
657d8e
   ClutterActorBox alloc;
657d8e
   CoglPipelineFilter filter;
657d8e
+  int main_layer_index;
657d8e
+  int backing_mask_layer_index;
657d8e
+  int backing_layer_index;
657d8e
+  int mask_layer_index;
657d8e
 
657d8e
   clutter_actor_get_scale (CLUTTER_ACTOR (stex), &tex_scale, NULL);
657d8e
   ensure_size_valid (stex);
657d8e
@@ -665,6 +775,12 @@ do_paint (MetaShapedTexture *stex,
657d8e
         }
657d8e
     }
657d8e
 
657d8e
+  get_layer_indices (stex,
657d8e
+                     &main_layer_index,
657d8e
+                     &backing_mask_layer_index,
657d8e
+                     &backing_layer_index,
657d8e
+                     &mask_layer_index);
657d8e
+
657d8e
   /* First, paint the unblended parts, which are part of the opaque region. */
657d8e
   if (use_opaque_region)
657d8e
     {
657d8e
@@ -686,8 +802,24 @@ do_paint (MetaShapedTexture *stex,
657d8e
       if (!cairo_region_is_empty (region))
657d8e
         {
657d8e
           opaque_pipeline = get_unblended_pipeline (stex, ctx);
657d8e
-          cogl_pipeline_set_layer_texture (opaque_pipeline, 0, paint_tex);
657d8e
-          cogl_pipeline_set_layer_filters (opaque_pipeline, 0, filter, filter);
657d8e
+          cogl_pipeline_set_layer_texture (opaque_pipeline, main_layer_index, paint_tex);
657d8e
+          cogl_pipeline_set_layer_filters (opaque_pipeline, main_layer_index, filter, filter);
657d8e
+
657d8e
+          if (stex->backing_store)
657d8e
+            {
657d8e
+              cogl_pipeline_set_layer_texture (opaque_pipeline,
657d8e
+                                               backing_mask_layer_index,
657d8e
+                                               stex->backing_store->mask_texture);
657d8e
+              cogl_pipeline_set_layer_filters (opaque_pipeline,
657d8e
+                                               backing_mask_layer_index,
657d8e
+                                               filter, filter);
657d8e
+              cogl_pipeline_set_layer_texture (opaque_pipeline,
657d8e
+                                               backing_layer_index,
657d8e
+                                               stex->backing_store->texture);
657d8e
+              cogl_pipeline_set_layer_filters (opaque_pipeline,
657d8e
+                                               backing_layer_index,
657d8e
+                                               filter, filter);
657d8e
+            }
657d8e
 
657d8e
           n_rects = cairo_region_num_rectangles (region);
657d8e
           for (i = 0; i < n_rects; i++)
657d8e
@@ -726,12 +858,28 @@ do_paint (MetaShapedTexture *stex,
657d8e
       else
657d8e
         {
657d8e
           blended_pipeline = get_masked_pipeline (stex, ctx);
657d8e
-          cogl_pipeline_set_layer_texture (blended_pipeline, 1, stex->mask_texture);
657d8e
-          cogl_pipeline_set_layer_filters (blended_pipeline, 1, filter, filter);
657d8e
+          cogl_pipeline_set_layer_texture (blended_pipeline, mask_layer_index, stex->mask_texture);
657d8e
+          cogl_pipeline_set_layer_filters (blended_pipeline, mask_layer_index, filter, filter);
657d8e
         }
657d8e
 
657d8e
-      cogl_pipeline_set_layer_texture (blended_pipeline, 0, paint_tex);
657d8e
-      cogl_pipeline_set_layer_filters (blended_pipeline, 0, filter, filter);
657d8e
+      cogl_pipeline_set_layer_texture (blended_pipeline, main_layer_index, paint_tex);
657d8e
+      cogl_pipeline_set_layer_filters (blended_pipeline, main_layer_index, filter, filter);
657d8e
+
657d8e
+      if (stex->backing_store)
657d8e
+        {
657d8e
+          cogl_pipeline_set_layer_texture (blended_pipeline,
657d8e
+                                           backing_mask_layer_index,
657d8e
+                                           stex->backing_store->mask_texture);
657d8e
+          cogl_pipeline_set_layer_filters (blended_pipeline,
657d8e
+                                           backing_mask_layer_index,
657d8e
+                                           filter, filter);
657d8e
+          cogl_pipeline_set_layer_texture (blended_pipeline,
657d8e
+                                           backing_layer_index,
657d8e
+                                           stex->backing_store->texture);
657d8e
+          cogl_pipeline_set_layer_filters (blended_pipeline,
657d8e
+                                           backing_layer_index,
657d8e
+                                           filter, filter);
657d8e
+        }
657d8e
 
657d8e
       CoglColor color;
657d8e
       cogl_color_init_from_4ub (&color, opacity, opacity, opacity, opacity);
657d8e
@@ -925,6 +1073,7 @@ meta_shaped_texture_set_mask_texture (MetaShapedTexture *stex,
657d8e
   g_return_if_fail (META_IS_SHAPED_TEXTURE (stex));
657d8e
 
657d8e
   g_clear_pointer (&stex->mask_texture, cogl_object_unref);
657d8e
+  g_clear_pointer (&stex->saved_mask_surface, cairo_surface_destroy);
657d8e
 
657d8e
   if (mask_texture != NULL)
657d8e
     {
657d8e
@@ -946,6 +1095,65 @@ meta_shaped_texture_is_obscured (MetaShapedTexture *stex)
657d8e
     return FALSE;
657d8e
 }
657d8e
 
657d8e
+static void
657d8e
+meta_texture_backing_store_redraw_mask (MetaTextureBackingStore *backing_store)
657d8e
+{
657d8e
+  CoglError *error = NULL;
657d8e
+
657d8e
+  if (!cogl_texture_set_data (backing_store->mask_texture, COGL_PIXEL_FORMAT_A_8,
657d8e
+                              cairo_image_surface_get_stride (backing_store->mask_surface),
657d8e
+                              cairo_image_surface_get_data (backing_store->mask_surface), 0,
657d8e
+                              &error))
657d8e
+    {
657d8e
+
657d8e
+      g_warning ("Failed to update backing mask texture");
657d8e
+      g_clear_pointer (&error, cogl_error_free);
657d8e
+    }
657d8e
+}
657d8e
+
657d8e
+static gboolean
657d8e
+meta_texture_backing_store_shrink (MetaTextureBackingStore     *backing_store,
657d8e
+                                   const cairo_rectangle_int_t *area)
657d8e
+{
657d8e
+  cairo_t *cr;
657d8e
+
657d8e
+  cairo_region_subtract_rectangle (backing_store->region, area);
657d8e
+
657d8e
+  /* If the client has finally redrawn the entire surface, we can
657d8e
+   * ditch our snapshot
657d8e
+   */
657d8e
+  if (cairo_region_is_empty (backing_store->region))
657d8e
+    return FALSE;
657d8e
+
657d8e
+  cr = cairo_create (backing_store->mask_surface);
657d8e
+  cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
657d8e
+  cairo_paint (cr);
657d8e
+  gdk_cairo_region (cr, backing_store->region);
657d8e
+  cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR);
657d8e
+  cairo_fill (cr);
657d8e
+  cairo_destroy (cr);
657d8e
+
657d8e
+  meta_texture_backing_store_redraw_mask (backing_store);
657d8e
+
657d8e
+  return TRUE;
657d8e
+}
657d8e
+
657d8e
+static void
657d8e
+shrink_backing_region (MetaShapedTexture           *stex,
657d8e
+                       const cairo_rectangle_int_t *area)
657d8e
+{
657d8e
+  gboolean still_backing_texture;
657d8e
+
657d8e
+  if (!stex->backing_store)
657d8e
+    return;
657d8e
+
657d8e
+  still_backing_texture =
657d8e
+      meta_texture_backing_store_shrink (stex->backing_store, area);
657d8e
+
657d8e
+  if (!still_backing_texture)
657d8e
+    disable_backing_store (stex);
657d8e
+}
657d8e
+
657d8e
 /**
657d8e
  * meta_shaped_texture_update_area:
657d8e
  * @stex: #MetaShapedTexture
657d8e
@@ -1041,6 +1249,8 @@ meta_shaped_texture_update_area (MetaShapedTexture *stex,
657d8e
                                      &clip);
657d8e
     }
657d8e
 
657d8e
+  shrink_backing_region (stex, &clip);
657d8e
+
657d8e
   meta_texture_tower_update_area (stex->paint_tower,
657d8e
                                   clip.x,
657d8e
                                   clip.y,
657d8e
@@ -1268,8 +1478,9 @@ should_get_via_offscreen (MetaShapedTexture *stex)
657d8e
 }
657d8e
 
657d8e
 static cairo_surface_t *
657d8e
-get_image_via_offscreen (MetaShapedTexture     *stex,
657d8e
-                         cairo_rectangle_int_t *clip)
657d8e
+get_image_via_offscreen (MetaShapedTexture      *stex,
657d8e
+                         cairo_rectangle_int_t  *clip,
657d8e
+                         CoglTexture           **texture)
657d8e
 {
657d8e
   ClutterBackend *clutter_backend = clutter_get_default_backend ();
657d8e
   CoglContext *cogl_context =
657d8e
@@ -1340,9 +1551,29 @@ get_image_via_offscreen (MetaShapedTexture     *stex,
657d8e
                                 clip->width, clip->height,
657d8e
                                 CLUTTER_CAIRO_FORMAT_ARGB32,
657d8e
                                 cairo_image_surface_get_data (surface));
657d8e
+  cairo_surface_mark_dirty (surface);
657d8e
+
657d8e
+  if (texture)
657d8e
+    {
657d8e
+      *texture = cogl_object_ref (image_texture);
657d8e
+
657d8e
+      if (G_UNLIKELY (meta_debug_show_backing_store))
657d8e
+        {
657d8e
+          cairo_t *cr;
657d8e
+
657d8e
+          cr = cairo_create (surface);
657d8e
+          cairo_set_source_rgba (cr, 1.0, 0.0, 0.0, 0.75);
657d8e
+          cairo_paint (cr);
657d8e
+          cairo_destroy (cr);
657d8e
+        }
657d8e
+
657d8e
+      cogl_texture_set_data (*texture, CLUTTER_CAIRO_FORMAT_ARGB32,
657d8e
+                             cairo_image_surface_get_stride (surface),
657d8e
+                             cairo_image_surface_get_data (surface), 0, NULL);
657d8e
+    }
657d8e
+
657d8e
   cogl_object_unref (fb);
657d8e
 
657d8e
-  cairo_surface_mark_dirty (surface);
657d8e
 
657d8e
   return surface;
657d8e
 }
657d8e
@@ -1404,7 +1635,7 @@ meta_shaped_texture_get_image (MetaShapedTexture     *stex,
657d8e
     }
657d8e
 
657d8e
   if (should_get_via_offscreen (stex))
657d8e
-    return get_image_via_offscreen (stex, transformed_clip);
657d8e
+    return get_image_via_offscreen (stex, transformed_clip, NULL);
657d8e
 
657d8e
   if (transformed_clip)
657d8e
     texture = cogl_texture_new_from_sub_texture (texture,
657d8e
@@ -1465,6 +1696,220 @@ meta_shaped_texture_get_image (MetaShapedTexture     *stex,
657d8e
   return surface;
657d8e
 }
657d8e
 
657d8e
+static void
657d8e
+meta_texture_backing_store_free (MetaTextureBackingStore *backing_store)
657d8e
+{
657d8e
+  g_clear_pointer (&backing_store->texture, cogl_object_unref);
657d8e
+  g_clear_pointer (&backing_store->mask_texture, cogl_object_unref);
657d8e
+  g_clear_pointer (&backing_store->mask_surface, cairo_surface_destroy);
657d8e
+  g_clear_pointer (&backing_store->region, cairo_region_destroy);
657d8e
+
657d8e
+  g_slice_free (MetaTextureBackingStore, backing_store);
657d8e
+}
657d8e
+
657d8e
+static MetaTextureBackingStore *
657d8e
+meta_texture_backing_store_new (CoglTexture *texture)
657d8e
+{
657d8e
+  MetaTextureBackingStore *backing_store = NULL;
657d8e
+  ClutterBackend *backend = clutter_get_default_backend ();
657d8e
+  CoglContext *context = clutter_backend_get_cogl_context (backend);
657d8e
+  CoglTexture *mask_texture = NULL;
657d8e
+  guchar *mask_data;
657d8e
+  int width, height, stride;
657d8e
+  cairo_surface_t *surface;
657d8e
+  cairo_region_t *region;
657d8e
+  cairo_rectangle_int_t backing_rectangle;
657d8e
+
657d8e
+  width = cogl_texture_get_width (texture);
657d8e
+  height = cogl_texture_get_height (texture);
657d8e
+  stride = cairo_format_stride_for_width (CAIRO_FORMAT_A8, width);
657d8e
+
657d8e
+  /* we start off by only letting the backing texture through, and none of the real texture */
657d8e
+  backing_rectangle.x = 0;
657d8e
+  backing_rectangle.y = 0;
657d8e
+  backing_rectangle.width = width;
657d8e
+  backing_rectangle.height = height;
657d8e
+
657d8e
+  region = cairo_region_create_rectangle (&backing_rectangle);
657d8e
+
657d8e
+  /* initialize mask to transparent, so the entire backing store shows through
657d8e
+   * up front
657d8e
+   */
657d8e
+  mask_data = g_malloc0 (stride * height);
657d8e
+  surface = cairo_image_surface_create_for_data (mask_data,
657d8e
+                                                 CAIRO_FORMAT_A8,
657d8e
+                                                 width,
657d8e
+                                                 height,
657d8e
+                                                 stride);
657d8e
+
657d8e
+  if (meta_texture_rectangle_check (texture))
657d8e
+    {
657d8e
+      mask_texture = COGL_TEXTURE (cogl_texture_rectangle_new_with_size (context,
657d8e
+                                                                         width,
657d8e
+                                                                         height));
657d8e
+      cogl_texture_set_components (mask_texture, COGL_TEXTURE_COMPONENTS_A);
657d8e
+      cogl_texture_set_region (mask_texture,
657d8e
+                               0, 0,
657d8e
+                               0, 0,
657d8e
+                               width, height,
657d8e
+                               width, height,
657d8e
+                               COGL_PIXEL_FORMAT_A_8,
657d8e
+                               stride, mask_data);
657d8e
+    }
657d8e
+  else
657d8e
+    {
657d8e
+      CoglError *error = NULL;
657d8e
+
657d8e
+      mask_texture = COGL_TEXTURE (cogl_texture_2d_new_from_data (context, width, height,
657d8e
+                                                                  COGL_PIXEL_FORMAT_A_8,
657d8e
+                                                                  stride, mask_data, &error));
657d8e
+
657d8e
+      if (error)
657d8e
+        {
657d8e
+          g_warning ("Failed to allocate mask texture: %s", error->message);
657d8e
+          cogl_error_free (error);
657d8e
+        }
657d8e
+    }
657d8e
+
657d8e
+  if (mask_texture)
657d8e
+    {
657d8e
+      backing_store = g_slice_new0 (MetaTextureBackingStore);
657d8e
+      backing_store->texture = cogl_object_ref (texture);
657d8e
+      backing_store->mask_texture = mask_texture;
657d8e
+      backing_store->mask_surface = surface;
657d8e
+      backing_store->region = region;
657d8e
+    }
657d8e
+
657d8e
+  return backing_store;
657d8e
+}
657d8e
+
657d8e
+static void
657d8e
+enable_backing_store (MetaShapedTexture *stex,
657d8e
+                      CoglTexture       *texture)
657d8e
+{
657d8e
+  g_clear_pointer (&stex->backing_store, meta_texture_backing_store_free);
657d8e
+
657d8e
+  stex->backing_store = meta_texture_backing_store_new (texture);
657d8e
+
657d8e
+  meta_shaped_texture_reset_pipelines (stex);
657d8e
+}
657d8e
+
657d8e
+static void
657d8e
+disable_backing_store (MetaShapedTexture *stex)
657d8e
+{
657d8e
+  g_clear_pointer (&stex->backing_store, meta_texture_backing_store_free);
657d8e
+
657d8e
+  meta_shaped_texture_reset_pipelines (stex);
657d8e
+}
657d8e
+
657d8e
+void
657d8e
+meta_shaped_texture_save (MetaShapedTexture *stex)
657d8e
+{
657d8e
+
657d8e
+  CoglTexture *texture, *mask_texture;
657d8e
+  cairo_surface_t *surface;
657d8e
+
657d8e
+  g_return_if_fail (META_IS_SHAPED_TEXTURE (stex));
657d8e
+
657d8e
+  texture = COGL_TEXTURE (stex->texture);
657d8e
+
657d8e
+  if (texture == NULL)
657d8e
+    return;
657d8e
+
657d8e
+  g_clear_pointer (&stex->saved_base_surface, cairo_surface_destroy);
657d8e
+  g_clear_pointer (&stex->saved_mask_surface, cairo_surface_destroy);
657d8e
+  g_clear_pointer (&stex->backing_store, meta_texture_backing_store_free);
657d8e
+
657d8e
+  if (should_get_via_offscreen (stex))
657d8e
+    {
657d8e
+      CoglTexture *backing_texture;
657d8e
+
657d8e
+      meta_shaped_texture_reset_pipelines (stex);
657d8e
+
657d8e
+      surface = get_image_via_offscreen (stex, NULL, &backing_texture);
657d8e
+
657d8e
+      enable_backing_store (stex, backing_texture);
657d8e
+      cogl_object_unref (backing_texture);
657d8e
+    }
657d8e
+  else
657d8e
+    {
657d8e
+      surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
657d8e
+                                            cogl_texture_get_width (texture),
657d8e
+                                            cogl_texture_get_height (texture));
657d8e
+
657d8e
+      cogl_texture_get_data (texture, CLUTTER_CAIRO_FORMAT_ARGB32,
657d8e
+                             cairo_image_surface_get_stride (surface),
657d8e
+                             cairo_image_surface_get_data (surface));
657d8e
+    }
657d8e
+
657d8e
+  stex->saved_base_surface = surface;
657d8e
+
657d8e
+  mask_texture = stex->mask_texture;
657d8e
+  if (mask_texture != NULL)
657d8e
+    {
657d8e
+      cairo_surface_t *mask_surface;
657d8e
+
657d8e
+      mask_surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
657d8e
+                                                 cogl_texture_get_width (mask_texture),
657d8e
+                                                 cogl_texture_get_height (mask_texture));
657d8e
+
657d8e
+      cogl_texture_get_data (mask_texture, CLUTTER_CAIRO_FORMAT_ARGB32,
657d8e
+                             cairo_image_surface_get_stride (mask_surface),
657d8e
+                             cairo_image_surface_get_data (mask_surface));
657d8e
+
657d8e
+      cairo_surface_mark_dirty (mask_surface);
657d8e
+
657d8e
+      stex->saved_mask_surface = mask_surface;
657d8e
+    }
657d8e
+}
657d8e
+
657d8e
+void
657d8e
+meta_shaped_texture_restore (MetaShapedTexture *stex)
657d8e
+{
657d8e
+  CoglTexture *texture;
657d8e
+  CoglError *error = NULL;
657d8e
+
657d8e
+  texture = meta_shaped_texture_get_texture (stex);
657d8e
+
657d8e
+  if (texture == NULL)
657d8e
+    return;
657d8e
+
657d8e
+  if (stex->mask_texture)
657d8e
+    {
657d8e
+      if (!cogl_texture_set_data (stex->mask_texture, CLUTTER_CAIRO_FORMAT_ARGB32,
657d8e
+                                  cairo_image_surface_get_stride (stex->saved_mask_surface),
657d8e
+                                  cairo_image_surface_get_data (stex->saved_mask_surface), 0,
657d8e
+                                  &error))
657d8e
+        {
657d8e
+          g_warning ("Failed to restore mask texture");
657d8e
+          g_clear_pointer (&error, cogl_error_free);
657d8e
+        }
657d8e
+      g_clear_pointer (&stex->saved_mask_surface, cairo_surface_destroy);
657d8e
+    }
657d8e
+
657d8e
+  /* if the main texture doesn't support direct writes, then
657d8e
+   * write to the local backing texture instead, and blend old
657d8e
+   * versus new at paint time.
657d8e
+   */
657d8e
+  if (stex->backing_store)
657d8e
+    {
657d8e
+      meta_texture_backing_store_redraw_mask (stex->backing_store);
657d8e
+      texture = stex->backing_store->texture;
657d8e
+    }
657d8e
+
657d8e
+  if (!cogl_texture_set_data (texture, CLUTTER_CAIRO_FORMAT_ARGB32,
657d8e
+                              cairo_image_surface_get_stride (stex->saved_base_surface),
657d8e
+                              cairo_image_surface_get_data (stex->saved_base_surface), 0,
657d8e
+                              &error))
657d8e
+    {
657d8e
+      g_warning ("Failed to restore texture");
657d8e
+      g_clear_pointer (&error, cogl_error_free);
657d8e
+    }
657d8e
+  g_clear_pointer (&stex->saved_base_surface, cairo_surface_destroy);
657d8e
+
657d8e
+  clutter_actor_queue_redraw (CLUTTER_ACTOR (stex));
657d8e
+}
657d8e
+
657d8e
 void
657d8e
 meta_shaped_texture_set_fallback_size (MetaShapedTexture *stex,
657d8e
                                        int                fallback_width,
657d8e
diff --git a/src/meta/meta-shaped-texture.h b/src/meta/meta-shaped-texture.h
657d8e
index c36b8547f..22b4fbd53 100644
657d8e
--- a/src/meta/meta-shaped-texture.h
657d8e
+++ b/src/meta/meta-shaped-texture.h
657d8e
@@ -66,6 +66,8 @@ META_EXPORT
657d8e
 cairo_surface_t * meta_shaped_texture_get_image (MetaShapedTexture     *stex,
657d8e
                                                  cairo_rectangle_int_t *clip);
657d8e
 
657d8e
+void meta_shaped_texture_save (MetaShapedTexture *self);
657d8e
+void meta_shaped_texture_restore (MetaShapedTexture *self);
657d8e
 G_END_DECLS
657d8e
 
657d8e
 #endif /* __META_SHAPED_TEXTURE_H__ */
657d8e
-- 
657d8e
2.21.0
657d8e