kathenas / rpms / mutter

Forked from rpms/mutter 5 years ago
Clone

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

88c283
From d8cc418899276b45cb1a787493e0998e3b008fe5 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
---
88c283
 src/compositor/meta-shaped-texture.c | 487 +++++++++++++++++++++++++--
776610
 src/meta/meta-shaped-texture.h       |   2 +
88c283
 2 files changed, 468 insertions(+), 21 deletions(-)
776610
776610
diff --git a/src/compositor/meta-shaped-texture.c b/src/compositor/meta-shaped-texture.c
88c283
index d64e214e5..ea8daa03d 100644
776610
--- a/src/compositor/meta-shaped-texture.c
776610
+++ b/src/compositor/meta-shaped-texture.c
88c283
@@ -40,7 +40,9 @@
88c283
 #include "compositor/meta-texture-tower.h"
88c283
 #include "compositor/region-utils.h"
776610
 #include "core/boxes-private.h"
776610
+#include <meta/meta-backend.h>
88c283
 #include "meta/meta-shaped-texture.h"
88c283
+#include "meta-texture-rectangle.h"
776610
 
88c283
 /* MAX_MIPMAPPING_FPS needs to be as small as possible for the best GPU
88c283
  * performance, but higher than the refresh rate of commonly slow updating
88c283
@@ -72,8 +74,12 @@ static void meta_shaped_texture_get_preferred_height (ClutterActor *self,
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
+
88c283
 enum
88c283
 {
776610
   SIZE_CHANGED,
88c283
@@ -83,6 +89,14 @@ enum
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
+
88c283
 struct _MetaShapedTexture
776610
 {
88c283
   ClutterActor parent;
88c283
@@ -114,6 +128,16 @@ struct _MetaShapedTexture
88c283
   int viewport_dst_width;
88c283
   int viewport_dst_height;
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
+
88c283
   int tex_width, tex_height;
88c283
   int fallback_width, fallback_height;
88c283
   int dst_width, dst_height;
88c283
@@ -148,6 +172,9 @@ meta_shaped_texture_class_init (MetaShapedTextureClass *klass)
776610
                                         0,
776610
                                         NULL, NULL, NULL,
776610
                                         G_TYPE_NONE, 0);
776610
+
776610
+  if (g_getenv ("MUTTER_DEBUG_BACKING_STORE"))
776610
+    meta_debug_show_backing_store = TRUE;
776610
 }
776610
 
776610
 static void
88c283
@@ -159,6 +186,11 @@ invalidate_size (MetaShapedTexture *stex)
88c283
 static void
88c283
 meta_shaped_texture_init (MetaShapedTexture *stex)
776610
 {
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);
88c283
+
88c283
   stex->paint_tower = meta_texture_tower_new ();
776610
 
88c283
   stex->texture = NULL;
88c283
@@ -171,6 +203,12 @@ meta_shaped_texture_init (MetaShapedTexture *stex)
88c283
                     "notify::scale-x",
88c283
                     G_CALLBACK (invalidate_size),
88c283
                     stex);
776610
+
776610
+  if (cogl_has_feature (cogl_context, COGL_FEATURE_ID_UNSTABLE_TEXTURES))
776610
+    {
88c283
+      g_signal_connect_object (backend, "suspending", G_CALLBACK (meta_shaped_texture_save), stex, G_CONNECT_SWAPPED);
88c283
+      g_signal_connect_object (backend, "resuming", G_CALLBACK (meta_shaped_texture_restore), stex, G_CONNECT_SWAPPED);
776610
+    }
776610
 }
776610
 
776610
 static void
88c283
@@ -311,24 +349,72 @@ meta_shaped_texture_dispose (GObject *object)
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
+  int next_layer_index = 0;
776610
+
776610
+  if (main_layer_index)
776610
+    *main_layer_index = next_layer_index;
776610
+
776610
+  next_layer_index++;
776610
+
88c283
+  if (stex->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
   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
 
88c283
   if (stex->base_pipeline)
88c283
     return stex->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
+
88c283
   if (!stex->is_y_inverted)
776610
     {
776610
       CoglMatrix matrix;
88c283
@@ -336,7 +422,22 @@ get_base_pipeline (MetaShapedTexture *stex,
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
+
88c283
+  if (stex->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
 
88c283
   if (stex->transform != META_MONITOR_TRANSFORM_NORMAL)
88c283
@@ -379,7 +480,7 @@ get_base_pipeline (MetaShapedTexture *stex,
88c283
     }
776610
 
88c283
   if (stex->snippet)
88c283
-    cogl_pipeline_add_layer_snippet (pipeline, 0, stex->snippet);
88c283
+    cogl_pipeline_add_layer_snippet (pipeline, main_layer_index, stex->snippet);
776610
 
88c283
   stex->base_pipeline = pipeline;
776610
 
88c283
@@ -398,12 +499,15 @@ get_masked_pipeline (MetaShapedTexture *stex,
776610
                      CoglContext       *ctx)
776610
 {
776610
   CoglPipeline *pipeline;
776610
+  int mask_layer_index;
776610
 
88c283
   if (stex->masked_pipeline)
88c283
     return stex->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
 
88c283
@@ -517,6 +621,8 @@ set_cogl_texture (MetaShapedTexture *stex,
88c283
   if (stex->texture)
88c283
     cogl_object_unref (stex->texture);
776610
 
88c283
+  g_clear_pointer (&stex->saved_base_surface, cairo_surface_destroy);
776610
+
88c283
   stex->texture = cogl_tex;
776610
 
776610
   if (cogl_tex != NULL)
88c283
@@ -579,6 +685,10 @@ do_paint (MetaShapedTexture *stex,
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
 
88c283
   clutter_actor_get_scale (CLUTTER_ACTOR (stex), &tex_scale, NULL);
88c283
   ensure_size_valid (stex);
88c283
@@ -665,6 +775,12 @@ do_paint (MetaShapedTexture *stex,
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
     {
88c283
@@ -686,8 +802,24 @@ do_paint (MetaShapedTexture *stex,
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
+
88c283
+          if (stex->backing_store)
776610
+            {
776610
+              cogl_pipeline_set_layer_texture (opaque_pipeline,
776610
+                                               backing_mask_layer_index,
88c283
+                                               stex->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,
88c283
+                                               stex->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++)
88c283
@@ -726,12 +858,28 @@ do_paint (MetaShapedTexture *stex,
776610
       else
776610
         {
776610
           blended_pipeline = get_masked_pipeline (stex, ctx);
88c283
-          cogl_pipeline_set_layer_texture (blended_pipeline, 1, stex->mask_texture);
776610
-          cogl_pipeline_set_layer_filters (blended_pipeline, 1, filter, filter);
88c283
+          cogl_pipeline_set_layer_texture (blended_pipeline, mask_layer_index, stex->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
+
88c283
+      if (stex->backing_store)
776610
+        {
776610
+          cogl_pipeline_set_layer_texture (blended_pipeline,
776610
+                                           backing_mask_layer_index,
88c283
+                                           stex->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,
88c283
+                                           stex->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);
88c283
@@ -925,6 +1073,7 @@ meta_shaped_texture_set_mask_texture (MetaShapedTexture *stex,
776610
   g_return_if_fail (META_IS_SHAPED_TEXTURE (stex));
776610
 
88c283
   g_clear_pointer (&stex->mask_texture, cogl_object_unref);
88c283
+  g_clear_pointer (&stex->saved_mask_surface, cairo_surface_destroy);
776610
 
776610
   if (mask_texture != NULL)
776610
     {
88c283
@@ -946,6 +1095,65 @@ meta_shaped_texture_is_obscured (MetaShapedTexture *stex)
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
+  gboolean still_backing_texture;
776610
+
88c283
+  if (!stex->backing_store)
776610
+    return;
776610
+
776610
+  still_backing_texture =
88c283
+      meta_texture_backing_store_shrink (stex->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
88c283
@@ -1041,6 +1249,8 @@ meta_shaped_texture_update_area (MetaShapedTexture *stex,
88c283
                                      &clip);
88c283
     }
776610
 
776610
+  shrink_backing_region (stex, &clip);
776610
+
88c283
   meta_texture_tower_update_area (stex->paint_tower,
88c283
                                   clip.x,
88c283
                                   clip.y,
88c283
@@ -1268,8 +1478,9 @@ should_get_via_offscreen (MetaShapedTexture *stex)
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
   ClutterBackend *clutter_backend = clutter_get_default_backend ();
776610
   CoglContext *cogl_context =
88c283
@@ -1340,9 +1551,29 @@ get_image_via_offscreen (MetaShapedTexture     *stex,
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
 }
88c283
@@ -1404,7 +1635,7 @@ meta_shaped_texture_get_image (MetaShapedTexture     *stex,
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,
88c283
@@ -1465,6 +1696,220 @@ meta_shaped_texture_get_image (MetaShapedTexture     *stex,
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
+{
88c283
+  g_clear_pointer (&stex->backing_store, meta_texture_backing_store_free);
776610
+
88c283
+  stex->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
+{
88c283
+  g_clear_pointer (&stex->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
+  cairo_surface_t *surface;
776610
+
776610
+  g_return_if_fail (META_IS_SHAPED_TEXTURE (stex));
776610
+
88c283
+  texture = COGL_TEXTURE (stex->texture);
776610
+
776610
+  if (texture == NULL)
776610
+    return;
776610
+
88c283
+  g_clear_pointer (&stex->saved_base_surface, cairo_surface_destroy);
88c283
+  g_clear_pointer (&stex->saved_mask_surface, cairo_surface_destroy);
88c283
+  g_clear_pointer (&stex->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
+
88c283
+  stex->saved_base_surface = surface;
776610
+
88c283
+  mask_texture = stex->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
+
88c283
+      stex->saved_mask_surface = mask_surface;
776610
+    }
776610
+}
776610
+
776610
+void
776610
+meta_shaped_texture_restore (MetaShapedTexture *stex)
776610
+{
776610
+  CoglTexture *texture;
776610
+  CoglError *error = NULL;
776610
+
776610
+  texture = meta_shaped_texture_get_texture (stex);
776610
+
776610
+  if (texture == NULL)
776610
+    return;
776610
+
88c283
+  if (stex->mask_texture)
776610
+    {
88c283
+      if (!cogl_texture_set_data (stex->mask_texture, CLUTTER_CAIRO_FORMAT_ARGB32,
88c283
+                                  cairo_image_surface_get_stride (stex->saved_mask_surface),
88c283
+                                  cairo_image_surface_get_data (stex->saved_mask_surface), 0,
776610
+                                  &error))
776610
+        {
776610
+          g_warning ("Failed to restore mask texture");
776610
+          g_clear_pointer (&error, cogl_error_free);
776610
+        }
88c283
+      g_clear_pointer (&stex->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
+   */
88c283
+  if (stex->backing_store)
776610
+    {
88c283
+      meta_texture_backing_store_redraw_mask (stex->backing_store);
88c283
+      texture = stex->backing_store->texture;
776610
+    }
776610
+
776610
+  if (!cogl_texture_set_data (texture, CLUTTER_CAIRO_FORMAT_ARGB32,
88c283
+                              cairo_image_surface_get_stride (stex->saved_base_surface),
88c283
+                              cairo_image_surface_get_data (stex->saved_base_surface), 0,
776610
+                              &error))
776610
+    {
776610
+      g_warning ("Failed to restore texture");
776610
+      g_clear_pointer (&error, cogl_error_free);
776610
+    }
88c283
+  g_clear_pointer (&stex->saved_base_surface, cairo_surface_destroy);
776610
+
776610
+  clutter_actor_queue_redraw (CLUTTER_ACTOR (stex));
776610
+}
776610
+
776610
 void
88c283
 meta_shaped_texture_set_fallback_size (MetaShapedTexture *stex,
88c283
                                        int                fallback_width,
776610
diff --git a/src/meta/meta-shaped-texture.h b/src/meta/meta-shaped-texture.h
88c283
index c36b8547f..22b4fbd53 100644
776610
--- a/src/meta/meta-shaped-texture.h
776610
+++ b/src/meta/meta-shaped-texture.h
88c283
@@ -66,6 +66,8 @@ META_EXPORT
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
-- 
88c283
2.21.0
776610