[mutter/wip/texture-purge-on-nvidia: 4/6] wip! compositor: save and restore textures on suspend
- From: Ray Strode <halfline src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [mutter/wip/texture-purge-on-nvidia: 4/6] wip! compositor: save and restore textures on suspend
- Date: Mon, 14 Jan 2019 00:45:20 +0000 (UTC)
commit 2ce895a50ed3f4090f9b9737c5c5d8256246a4c7
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 | 422 +++++++++++++++++++++++++++++++++--
src/meta/meta-shaped-texture.h | 2 +
2 files changed, 404 insertions(+), 20 deletions(-)
---
diff --git a/src/compositor/meta-shaped-texture.c b/src/compositor/meta-shaped-texture.c
index 6d8bfe989..c5eda31e3 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,25 +231,74 @@ meta_shaped_texture_dispose (GObject *object)
G_OBJECT_CLASS (meta_shaped_texture_parent_class)->dispose (object);
}
+static int
+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;
+
+ return 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;
+ int i, number_of_layers;
if (priv->base_pipeline)
return priv->base_pipeline;
pipeline = cogl_pipeline_new (ctx);
- cogl_pipeline_set_layer_wrap_mode_s (pipeline, 0,
- COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE);
- cogl_pipeline_set_layer_wrap_mode_t (pipeline, 0,
- COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE);
- cogl_pipeline_set_layer_wrap_mode_s (pipeline, 1,
- COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE);
- cogl_pipeline_set_layer_wrap_mode_t (pipeline, 1,
- COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE);
+
+ number_of_layers = get_layer_indices (stex,
+ &main_layer_index,
+ &backing_mask_layer_index,
+ &backing_layer_index,
+ NULL);
+
+ for (i = 0; i < number_of_layers; i++)
+ {
+ cogl_pipeline_set_layer_wrap_mode_s (pipeline, i,
+ COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE);
+ cogl_pipeline_set_layer_wrap_mode_t (pipeline, i,
+ COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE);
+ }
+
if (!priv->is_y_inverted)
{
CoglMatrix matrix;
@@ -236,11 +306,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 +345,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 +473,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 +539,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 +566,17 @@ 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);
+
+ if (priv->backing_texture)
+ {
+ cogl_pipeline_set_layer_texture (opaque_pipeline, backing_mask_layer_index,
priv->backing_mask_texture);
+ cogl_pipeline_set_layer_filters (opaque_pipeline, backing_mask_layer_index, filter, filter);
+
+ cogl_pipeline_set_layer_texture (opaque_pipeline, backing_layer_index, priv->backing_texture);
+ cogl_pipeline_set_layer_filters (opaque_pipeline, backing_layer_index, filter, filter);
+ }
n_rects = cairo_region_num_rectangles (region);
for (i = 0; i < n_rects; i++)
@@ -504,12 +611,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 +862,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 +933,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 +1027,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 +1081,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 +1144,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,9 +1178,18 @@ get_image_via_offscreen (MetaShapedTexture *stex,
clip->width, clip->height,
CLUTTER_CAIRO_FORMAT_ARGB32,
cairo_image_surface_get_data (surface));
+ cairo_surface_mark_dirty (surface);
+
+ if (texture)
+ {
+ *texture = cogl_object_ref (image_texture);
+ cogl_texture_set_data (*texture, CLUTTER_CAIRO_FORMAT_ARGB32,
+ cairo_image_surface_get_stride (surface),
+ cairo_image_surface_get_data (surface), 0, NULL);
+ }
+
cogl_object_unref (fb);
- cairo_surface_mark_dirty (surface);
return surface;
}
@@ -1078,7 +1250,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 +1311,216 @@ meta_shaped_texture_get_image (MetaShapedTexture *stex,
return surface;
}
+static void
+enable_backing_store (MetaShapedTexture *stex,
+ CoglTexture *texture)
+{
+ 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;
+ }
+
+ priv->backing_texture = cogl_object_ref (texture);
+
+ 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))
+ {
+ CoglTexture *backing_texture;
+
+ meta_shaped_texture_reset_pipelines (stex);
+
+ surface = get_image_via_offscreen (stex, NULL, &backing_texture);
+
+ enable_backing_store (stex, backing_texture);
+ cogl_object_unref (backing_texture);
+ }
+ 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));
+ }
+
+ 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);
+ }
+
+ clutter_actor_queue_redraw (CLUTTER_ACTOR (stex));
+}
+
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]