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