[mutter/wip/texture-purge-on-nvidia: 17/17] wip! compositor: save and restore textures on suspend



commit f02f0e3dc14d6f46e38c519da8bdd4e97638b763
Author: Ray Strode <rstrode redhat com>
Date:   Thu Jan 10 10:48:02 2019 -0500

    wip! compositor: save and restore textures on suspend

 src/compositor/meta-shaped-texture.c | 387 ++++++++++++++++++++++++++++++++++-
 src/meta/meta-shaped-texture.h       |   2 +
 2 files changed, 378 insertions(+), 11 deletions(-)
---
diff --git a/src/compositor/meta-shaped-texture.c b/src/compositor/meta-shaped-texture.c
index 6d8bfe989..9063cf036 100644
--- a/src/compositor/meta-shaped-texture.c
+++ b/src/compositor/meta-shaped-texture.c
@@ -29,6 +29,7 @@
 
 #include <meta/meta-shaped-texture.h>
 #include "meta-shaped-texture-private.h"
+#include "meta-texture-rectangle.h"
 
 #include <cogl/cogl.h>
 #include <gdk/gdk.h> /* for gdk_rectangle_intersect() */
@@ -38,6 +39,7 @@
 #include "core/boxes-private.h"
 
 #include "meta-cullable.h"
+#include <meta/meta-backend.h>
 
 static void meta_shaped_texture_dispose  (GObject    *object);
 
@@ -55,6 +57,8 @@ static void meta_shaped_texture_get_preferred_height (ClutterActor *self,
 
 static gboolean meta_shaped_texture_get_paint_volume (ClutterActor *self, ClutterPaintVolume *volume);
 
+static void disable_backing_store (MetaShapedTexture *stex);
+
 static void cullable_iface_init (MetaCullableInterface *iface);
 
 G_DEFINE_TYPE_WITH_CODE (MetaShapedTexture, meta_shaped_texture, CLUTTER_TYPE_ACTOR,
@@ -93,6 +97,19 @@ struct _MetaShapedTexturePrivate
   cairo_region_t *clip_region;
   cairo_region_t *unobscured_region;
 
+  /* textures get corrupted on suspend, so save them */
+  cairo_surface_t *saved_base_surface;
+  cairo_surface_t *saved_mask_surface;
+
+  /* We can't just restore external textures, so we need to track
+   * which parts of the external texture are freshly drawn from
+   * the client after corruption, and fill in the rest from our
+   * saved snapshot */
+  cairo_surface_t *backing_mask_surface;
+  CoglTexture *backing_texture;
+  CoglTexture *backing_mask_texture;
+  cairo_region_t *backing_region;
+
   guint tex_width, tex_height;
   guint fallback_width, fallback_height;
 
@@ -126,6 +143,7 @@ static void
 meta_shaped_texture_init (MetaShapedTexture *self)
 {
   MetaShapedTexturePrivate *priv;
+  MetaBackend *backend = meta_get_backend ();
 
   priv = self->priv = META_SHAPED_TEXTURE_GET_PRIVATE (self);
 
@@ -135,6 +153,9 @@ meta_shaped_texture_init (MetaShapedTexture *self)
   priv->mask_texture = NULL;
   priv->create_mipmaps = TRUE;
   priv->is_y_inverted = TRUE;
+
+  g_signal_connect_object (backend, "suspending", G_CALLBACK (meta_shaped_texture_save), self, 
G_CONNECT_SWAPPED);
+  g_signal_connect_object (backend, "resuming", G_CALLBACK (meta_shaped_texture_restore), self, 
G_CONNECT_SWAPPED);
 }
 
 static void
@@ -210,12 +231,51 @@ meta_shaped_texture_dispose (GObject *object)
   G_OBJECT_CLASS (meta_shaped_texture_parent_class)->dispose (object);
 }
 
+static void
+get_layer_indices (MetaShapedTexture *stex,
+                   int               *main_layer_index,
+                   int               *backing_mask_layer_index,
+                   int               *backing_layer_index,
+                   int               *mask_layer_index)
+{
+  MetaShapedTexturePrivate *priv = stex->priv;
+  int next_layer_index = 0;
+
+  if (main_layer_index)
+    *main_layer_index = next_layer_index;
+
+  next_layer_index++;
+
+  if (priv->backing_texture)
+    {
+      if (backing_mask_layer_index)
+        *backing_mask_layer_index = next_layer_index;
+      next_layer_index++;
+      if (backing_layer_index)
+        *backing_layer_index = next_layer_index;
+      next_layer_index++;
+    }
+  else
+    {
+      if (backing_mask_layer_index)
+        *backing_mask_layer_index = -1;
+      if (backing_layer_index)
+        *backing_layer_index = -1;
+    }
+
+  if (mask_layer_index)
+    *mask_layer_index = next_layer_index;
+}
+
 static CoglPipeline *
 get_base_pipeline (MetaShapedTexture *stex,
                    CoglContext       *ctx)
 {
   MetaShapedTexturePrivate *priv = stex->priv;
   CoglPipeline *pipeline;
+  int main_layer_index;
+  int backing_layer_index;
+  int backing_mask_layer_index;
 
   if (priv->base_pipeline)
     return priv->base_pipeline;
@@ -229,6 +289,17 @@ get_base_pipeline (MetaShapedTexture *stex,
                                        COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE);
   cogl_pipeline_set_layer_wrap_mode_t (pipeline, 1,
                                        COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE);
+  cogl_pipeline_set_layer_wrap_mode_s (pipeline, 2,
+                                       COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE);
+  cogl_pipeline_set_layer_wrap_mode_t (pipeline, 2,
+                                       COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE);
+
+  get_layer_indices (stex,
+                     &main_layer_index,
+                     &backing_mask_layer_index,
+                     &backing_layer_index,
+                     NULL);
+
   if (!priv->is_y_inverted)
     {
       CoglMatrix matrix;
@@ -236,11 +307,26 @@ get_base_pipeline (MetaShapedTexture *stex,
       cogl_matrix_init_identity (&matrix);
       cogl_matrix_scale (&matrix, 1, -1, 1);
       cogl_matrix_translate (&matrix, 0, -1, 0);
-      cogl_pipeline_set_layer_matrix (pipeline, 0, &matrix);
+      cogl_pipeline_set_layer_matrix (pipeline, main_layer_index, &matrix);
+    }
+
+  if (priv->backing_texture)
+    {
+      char *backing_description;
+      cogl_pipeline_set_layer_combine (pipeline, backing_mask_layer_index,
+                                       "RGBA = REPLACE(PREVIOUS)",
+                                       NULL);
+      backing_description = g_strdup_printf ("RGBA = INTERPOLATE(PREVIOUS, TEXTURE_%d, TEXTURE_%d[A])",
+                                             backing_layer_index,
+                                             backing_mask_layer_index);
+      cogl_pipeline_set_layer_combine (pipeline, backing_layer_index,
+                                       backing_description,
+                                       NULL);
+      g_free (backing_description);
     }
 
   if (priv->snippet)
-    cogl_pipeline_add_layer_snippet (pipeline, 0, priv->snippet);
+    cogl_pipeline_add_layer_snippet (pipeline, main_layer_index, priv->snippet);
 
   priv->base_pipeline = pipeline;
 
@@ -260,12 +346,15 @@ get_masked_pipeline (MetaShapedTexture *stex,
 {
   MetaShapedTexturePrivate *priv = stex->priv;
   CoglPipeline *pipeline;
+  int mask_layer_index;
 
   if (priv->masked_pipeline)
     return priv->masked_pipeline;
 
+  get_layer_indices (stex, NULL, NULL, NULL, &mask_layer_index);
+
   pipeline = cogl_pipeline_copy (get_base_pipeline (stex, ctx));
-  cogl_pipeline_set_layer_combine (pipeline, 1,
+  cogl_pipeline_set_layer_combine (pipeline, mask_layer_index,
                                    "RGBA = MODULATE (PREVIOUS, TEXTURE[A])",
                                    NULL);
 
@@ -385,6 +474,10 @@ do_paint (MetaShapedTexture *stex,
   CoglContext *ctx;
   ClutterActorBox alloc;
   CoglPipelineFilter filter;
+  int main_layer_index;
+  int backing_mask_layer_index;
+  int backing_layer_index;
+  int mask_layer_index;
 
   tex_width = priv->tex_width;
   tex_height = priv->tex_height;
@@ -447,6 +540,12 @@ do_paint (MetaShapedTexture *stex,
         }
     }
 
+  get_layer_indices (stex,
+                     &main_layer_index,
+                     &backing_mask_layer_index,
+                     &backing_layer_index,
+                     &mask_layer_index);
+
   /* First, paint the unblended parts, which are part of the opaque region. */
   if (use_opaque_region)
     {
@@ -468,8 +567,8 @@ do_paint (MetaShapedTexture *stex,
       if (!cairo_region_is_empty (region))
         {
           opaque_pipeline = get_unblended_pipeline (stex, ctx);
-          cogl_pipeline_set_layer_texture (opaque_pipeline, 0, paint_tex);
-          cogl_pipeline_set_layer_filters (opaque_pipeline, 0, filter, filter);
+          cogl_pipeline_set_layer_texture (opaque_pipeline, main_layer_index, paint_tex);
+          cogl_pipeline_set_layer_filters (opaque_pipeline, main_layer_index, filter, filter);
 
           n_rects = cairo_region_num_rectangles (region);
           for (i = 0; i < n_rects; i++)
@@ -504,12 +603,21 @@ do_paint (MetaShapedTexture *stex,
       else
         {
           blended_pipeline = get_masked_pipeline (stex, ctx);
-          cogl_pipeline_set_layer_texture (blended_pipeline, 1, priv->mask_texture);
-          cogl_pipeline_set_layer_filters (blended_pipeline, 1, filter, filter);
+          cogl_pipeline_set_layer_texture (blended_pipeline, mask_layer_index, priv->mask_texture);
+          cogl_pipeline_set_layer_filters (blended_pipeline, mask_layer_index, filter, filter);
         }
 
-      cogl_pipeline_set_layer_texture (blended_pipeline, 0, paint_tex);
-      cogl_pipeline_set_layer_filters (blended_pipeline, 0, filter, filter);
+      cogl_pipeline_set_layer_texture (blended_pipeline, main_layer_index, paint_tex);
+      cogl_pipeline_set_layer_filters (blended_pipeline, main_layer_index, filter, filter);
+
+      if (priv->backing_texture)
+        {
+          cogl_pipeline_set_layer_texture (blended_pipeline, backing_mask_layer_index, 
priv->backing_mask_texture);
+          cogl_pipeline_set_layer_filters (blended_pipeline, backing_mask_layer_index, filter, filter);
+
+          cogl_pipeline_set_layer_texture (blended_pipeline, backing_layer_index, priv->backing_texture);
+          cogl_pipeline_set_layer_filters (blended_pipeline, backing_layer_index, filter, filter);
+        }
 
       CoglColor color;
       cogl_color_init_from_4ub (&color, opacity, opacity, opacity, opacity);
@@ -746,6 +854,48 @@ meta_shaped_texture_is_obscured (MetaShapedTexture *self)
     return FALSE;
 }
 
+static void
+reduce_backing_region (MetaShapedTexture           *stex,
+                       const cairo_rectangle_int_t *area)
+{
+  MetaShapedTexturePrivate *priv = stex->priv;
+  CoglError *error = NULL;
+  cairo_t *cr;
+
+  if (!priv->backing_region)
+    return;
+
+  cairo_region_subtract_rectangle (priv->backing_region, area);
+
+  /* If the client has finally redrawn the entire surface, we can
+   * ditch our snapshot
+   */
+  if (cairo_region_is_empty (priv->backing_region))
+    {
+      disable_backing_store (stex);
+      return;
+    }
+
+  cr = cairo_create (priv->backing_mask_surface);
+  cairo_paint (cr);
+  gdk_cairo_region (cr, priv->backing_region);
+  cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR);
+  cairo_fill (cr);
+  cairo_destroy (cr);
+
+  if (!cogl_texture_set_data (priv->backing_mask_texture, COGL_PIXEL_FORMAT_A_8,
+                              cairo_image_surface_get_stride (priv->backing_mask_surface),
+                              cairo_image_surface_get_data (priv->backing_mask_surface), 0,
+                              &error))
+    {
+
+      g_warning ("Failed to update backing mask texture");
+      cogl_error_free (error);
+      error = NULL;
+      return;
+    }
+}
+
 /**
  * meta_shaped_texture_update_area:
  * @stex: #MetaShapedTexture
@@ -775,6 +925,8 @@ meta_shaped_texture_update_area (MetaShapedTexture *stex,
   if (priv->texture == NULL)
     return FALSE;
 
+  reduce_backing_region (stex, &clip);
+
   meta_texture_tower_update_area (priv->paint_tower, x, y, width, height);
 
   unobscured_region = effective_unobscured_region (stex);
@@ -867,6 +1019,7 @@ CoglTexture *
 meta_shaped_texture_get_texture (MetaShapedTexture *stex)
 {
   g_return_val_if_fail (META_IS_SHAPED_TEXTURE (stex), NULL);
+
   return COGL_TEXTURE (stex->priv->texture);
 }
 
@@ -920,7 +1073,8 @@ should_get_via_offscreen (MetaShapedTexture *stex)
 
 static cairo_surface_t *
 get_image_via_offscreen (MetaShapedTexture     *stex,
-                         cairo_rectangle_int_t *clip)
+                         cairo_rectangle_int_t *clip,
+                         CoglTexture           **texture)
 {
   MetaShapedTexturePrivate *priv = stex->priv;
   ClutterBackend *clutter_backend = clutter_get_default_backend ();
@@ -982,6 +1136,7 @@ get_image_via_offscreen (MetaShapedTexture     *stex,
 
   offscreen = cogl_offscreen_new_with_texture (COGL_TEXTURE (image_texture));
   fb = COGL_FRAMEBUFFER (offscreen);
+
   cogl_object_unref (image_texture);
   if (!cogl_framebuffer_allocate (fb, &error))
     {
@@ -1015,6 +1170,10 @@ get_image_via_offscreen (MetaShapedTexture     *stex,
                                 clip->width, clip->height,
                                 CLUTTER_CAIRO_FORMAT_ARGB32,
                                 cairo_image_surface_get_data (surface));
+
+  if (texture)
+    *texture = cogl_object_ref (image_texture);
+
   cogl_object_unref (fb);
 
   cairo_surface_mark_dirty (surface);
@@ -1078,7 +1237,7 @@ meta_shaped_texture_get_image (MetaShapedTexture     *stex,
     }
 
   if (should_get_via_offscreen (stex))
-    return get_image_via_offscreen (stex, transformed_clip);
+    return get_image_via_offscreen (stex, transformed_clip, NULL);
 
   if (transformed_clip)
     texture = cogl_texture_new_from_sub_texture (texture,
@@ -1139,6 +1298,212 @@ meta_shaped_texture_get_image (MetaShapedTexture     *stex,
   return surface;
 }
 
+static void
+enable_backing_store (MetaShapedTexture *stex)
+{
+  MetaShapedTexturePrivate *priv = stex->priv;
+  ClutterBackend *backend = clutter_get_default_backend ();
+  CoglContext *context = clutter_backend_get_cogl_context (backend);
+  CoglTexture *mask_texture = NULL;
+  guchar *mask_data;
+  int stride;
+  cairo_surface_t *surface;
+  cairo_region_t *region;
+  cairo_rectangle_int_t backing_rectangle;
+
+  g_clear_pointer (&priv->backing_mask_texture, cogl_object_unref);
+  g_clear_pointer (&priv->backing_mask_surface, cairo_surface_destroy);
+  g_clear_pointer (&priv->backing_region, cairo_region_destroy);
+
+  /* we start off by only letting the backing texture through, and none of the real texture */
+  backing_rectangle.x = 0;
+  backing_rectangle.y = 0;
+  backing_rectangle.width = priv->tex_width;
+  backing_rectangle.height = priv->tex_height;
+
+  region = cairo_region_create_rectangle (&backing_rectangle);
+
+  stride = cairo_format_stride_for_width (CAIRO_FORMAT_A8, priv->tex_width);
+  mask_data = g_malloc0 (stride * priv->tex_height);
+  surface = cairo_image_surface_create_for_data (mask_data,
+                                                 CAIRO_FORMAT_A8,
+                                                 priv->tex_width,
+                                                 priv->tex_height,
+                                                 stride);
+
+  if (meta_texture_rectangle_check (priv->texture))
+    {
+      mask_texture = COGL_TEXTURE (cogl_texture_rectangle_new_with_size (context,
+                                                                         priv->tex_width,
+                                                                         priv->tex_height));
+      cogl_texture_set_components (mask_texture, COGL_TEXTURE_COMPONENTS_A);
+      cogl_texture_set_region (mask_texture,
+                               0, 0,
+                               0, 0,
+                               priv->tex_width, priv->tex_height,
+                               priv->tex_width, priv->tex_height,
+                               COGL_PIXEL_FORMAT_A_8,
+                               stride, mask_data);
+    }
+  else
+    {
+      CoglError *error = NULL;
+
+      mask_texture = COGL_TEXTURE (cogl_texture_2d_new_from_data (context, priv->tex_width, priv->tex_height,
+                                                                  COGL_PIXEL_FORMAT_A_8,
+                                                                  stride, mask_data, &error));
+
+      if (error)
+        {
+          g_warning ("Failed to allocate mask texture: %s", error->message);
+          cogl_error_free (error);
+        }
+    }
+
+  if (mask_texture)
+    {
+      priv->backing_mask_texture = mask_texture;
+      priv->backing_mask_surface = surface;
+      priv->backing_region = region;
+    }
+
+  meta_shaped_texture_reset_pipelines (stex);
+}
+
+static void
+disable_backing_store (MetaShapedTexture *stex)
+{
+  MetaShapedTexturePrivate *priv = stex->priv;
+
+  g_clear_pointer (&priv->backing_texture, cogl_object_unref);
+  g_clear_pointer (&priv->backing_mask_texture, cogl_object_unref);
+  g_clear_pointer (&priv->backing_mask_surface, cairo_surface_destroy);
+  g_clear_pointer (&priv->backing_region, cairo_region_destroy);
+
+  meta_shaped_texture_reset_pipelines (stex);
+}
+
+void
+meta_shaped_texture_save (MetaShapedTexture *stex)
+{
+
+  CoglTexture *texture, *mask_texture;
+  MetaShapedTexturePrivate *priv = stex->priv;
+  cairo_surface_t *surface;
+
+  g_return_if_fail (META_IS_SHAPED_TEXTURE (stex));
+
+  texture = COGL_TEXTURE (priv->texture);
+
+  if (texture == NULL)
+    return;
+
+  g_clear_pointer (&priv->saved_base_surface, cairo_surface_destroy);
+  g_clear_pointer (&priv->saved_mask_surface, cairo_surface_destroy);
+  g_clear_pointer (&priv->backing_texture, cogl_object_unref);
+
+  if (should_get_via_offscreen (stex))
+    {
+      meta_shaped_texture_reset_pipelines (stex);
+
+      surface = get_image_via_offscreen (stex, NULL, &priv->backing_texture);
+      enable_backing_store (stex);
+      meta_shaped_texture_reset_pipelines (stex);
+
+      clutter_actor_queue_redraw (CLUTTER_ACTOR (stex));
+    }
+  else
+    {
+      surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
+                                            cogl_texture_get_width (texture),
+                                            cogl_texture_get_height (texture));
+
+      cogl_texture_get_data (texture, CLUTTER_CAIRO_FORMAT_ARGB32,
+                             cairo_image_surface_get_stride (surface),
+                             cairo_image_surface_get_data (surface));
+
+      cairo_surface_mark_dirty (surface);
+    }
+
+  priv->saved_base_surface = surface;
+
+  mask_texture = stex->priv->mask_texture;
+  if (mask_texture != NULL)
+    {
+      cairo_surface_t *mask_surface;
+
+      mask_surface = cairo_image_surface_create (CAIRO_FORMAT_A8,
+                                                 cogl_texture_get_width (mask_texture),
+                                                 cogl_texture_get_height (mask_texture));
+
+      cogl_texture_get_data (mask_texture, COGL_PIXEL_FORMAT_A_8,
+                             cairo_image_surface_get_stride (mask_surface),
+                             cairo_image_surface_get_data (mask_surface));
+
+      cairo_surface_mark_dirty (mask_surface);
+
+      priv->saved_mask_surface = mask_surface;
+    }
+}
+
+void
+meta_shaped_texture_restore (MetaShapedTexture *stex)
+{
+  MetaShapedTexturePrivate *priv = stex->priv;
+  CoglTexture *texture;
+  CoglError *error = NULL;
+
+  texture = meta_shaped_texture_get_texture (stex);
+
+  if (texture == NULL)
+    return;
+
+  /* if the main texture doesn't support direct writes, then
+   * write to the local backing texture instead, and blend old
+   * versus new at paint time.
+   */
+  if (priv->backing_texture)
+    texture = priv->backing_texture;
+
+  if (!cogl_texture_set_data (texture, CLUTTER_CAIRO_FORMAT_ARGB32,
+                              cairo_image_surface_get_stride (priv->saved_base_surface),
+                              cairo_image_surface_get_data (priv->saved_base_surface), 0,
+                              &error))
+    {
+      g_warning ("Failed to restore texture");
+      cogl_error_free (error);
+      error = NULL;
+    }
+  g_clear_pointer (&priv->saved_base_surface, cairo_surface_destroy);
+
+  if (priv->backing_mask_texture)
+    {
+      if (!cogl_texture_set_data (priv->backing_mask_texture, CAIRO_FORMAT_A8,
+                                  cairo_image_surface_get_stride (priv->backing_mask_surface),
+                                  cairo_image_surface_get_data (priv->backing_mask_surface), 0,
+                                  &error))
+      {
+          g_warning ("Failed to restore texture");
+          cogl_error_free (error);
+          error = NULL;
+      }
+    }
+
+  if (priv->mask_texture)
+    {
+      if (!cogl_texture_set_data (priv->mask_texture, CAIRO_FORMAT_A8,
+                                  cairo_image_surface_get_stride (priv->saved_mask_surface),
+                                  cairo_image_surface_get_data (priv->saved_mask_surface), 0,
+                                  &error))
+      {
+          g_warning ("Failed to restore mask texture");
+          cogl_error_free (error);
+          error = NULL;
+      }
+      g_clear_pointer (&priv->saved_mask_surface, cairo_surface_destroy);
+    }
+}
+
 void
 meta_shaped_texture_set_fallback_size (MetaShapedTexture *self,
                                        guint              fallback_width,
diff --git a/src/meta/meta-shaped-texture.h b/src/meta/meta-shaped-texture.h
index 80b23f2ea..fc0567c9f 100644
--- a/src/meta/meta-shaped-texture.h
+++ b/src/meta/meta-shaped-texture.h
@@ -81,6 +81,8 @@ void meta_shaped_texture_set_opaque_region (MetaShapedTexture *stex,
 cairo_surface_t * meta_shaped_texture_get_image (MetaShapedTexture     *stex,
                                                  cairo_rectangle_int_t *clip);
 
+void meta_shaped_texture_save (MetaShapedTexture *self);
+void meta_shaped_texture_restore (MetaShapedTexture *self);
 G_END_DECLS
 
 #endif /* __META_SHAPED_TEXTURE_H__ */


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