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

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