[mutter/wip/texture-purge-on-nvidia: 10/17] shaped-texture: Draw external textures via offscreen



commit 8c3a853f6bfe71589c11b1da1a84e5abef9f8b0f
Author: Jonas Ã…dahl <jadahl gmail com>
Date:   Thu Dec 20 17:34:18 2018 +0100

    shaped-texture: Draw external textures via offscreen
    
    EGLStream textures are imported as GL_TEXTURE_EXTERNAL_OES and reading
    pixels directly from them is not supported. To make it possible to get
    pixels, create an offscreen framebuffer and paint the actor to it, then
    read pixels from the framebuffer instead of the texture directly.
    
    https://gitlab.gnome.org/GNOME/mutter/merge_requests/362

 src/compositor/meta-shaped-texture.c | 136 ++++++++++++++++++++++++++++++++++-
 1 file changed, 134 insertions(+), 2 deletions(-)
---
diff --git a/src/compositor/meta-shaped-texture.c b/src/compositor/meta-shaped-texture.c
index 1876ef9a2..6d8bfe989 100644
--- a/src/compositor/meta-shaped-texture.c
+++ b/src/compositor/meta-shaped-texture.c
@@ -35,6 +35,7 @@
 
 #include "clutter-utils.h"
 #include "meta-texture-tower.h"
+#include "core/boxes-private.h"
 
 #include "meta-cullable.h"
 
@@ -906,6 +907,121 @@ meta_shaped_texture_get_opaque_region (MetaShapedTexture *stex)
   return priv->opaque_region;
 }
 
+static gboolean
+should_get_via_offscreen (MetaShapedTexture *stex)
+{
+  MetaShapedTexturePrivate *priv = stex->priv;
+
+  if (!cogl_texture_is_get_data_supported (priv->texture))
+    return TRUE;
+
+  return FALSE;
+}
+
+static cairo_surface_t *
+get_image_via_offscreen (MetaShapedTexture     *stex,
+                         cairo_rectangle_int_t *clip)
+{
+  MetaShapedTexturePrivate *priv = stex->priv;
+  ClutterBackend *clutter_backend = clutter_get_default_backend ();
+  CoglContext *cogl_context =
+    clutter_backend_get_cogl_context (clutter_backend);
+  CoglTexture *image_texture;
+  GError *error = NULL;
+  CoglOffscreen *offscreen;
+  CoglFramebuffer *fb;
+  CoglMatrix projection_matrix;
+  unsigned int fb_width, fb_height;
+  cairo_rectangle_int_t fallback_clip;
+  CoglColor clear_color;
+  cairo_surface_t *surface;
+
+  if (cogl_has_feature (cogl_context, COGL_FEATURE_ID_TEXTURE_NPOT))
+    {
+      fb_width = priv->tex_width;
+      fb_height = priv->tex_height;
+    }
+  else
+    {
+      fb_width = clutter_util_next_p2 (priv->tex_width);
+      fb_height = clutter_util_next_p2 (priv->tex_height);
+    }
+
+  if (!clip)
+    {
+      fallback_clip = (cairo_rectangle_int_t) {
+        .width = priv->tex_width,
+        .height = priv->tex_height,
+      };
+      clip = &fallback_clip;
+    }
+
+  image_texture =
+    COGL_TEXTURE (cogl_texture_2d_new_with_size (cogl_context,
+                                                 fb_width, fb_height));
+  cogl_primitive_texture_set_auto_mipmap (COGL_PRIMITIVE_TEXTURE (image_texture),
+                                          FALSE);
+  if (!cogl_texture_allocate (COGL_TEXTURE (image_texture), &error))
+    {
+      g_error_free (error);
+      cogl_object_unref (image_texture);
+      return FALSE;
+    }
+
+  if (fb_width != priv->tex_width || fb_height != priv->tex_height)
+    {
+      CoglSubTexture *sub_texture;
+
+      sub_texture = cogl_sub_texture_new (cogl_context,
+                                          image_texture,
+                                          0, 0,
+                                          priv->tex_width, priv->tex_height);
+      cogl_object_unref (image_texture);
+      image_texture = COGL_TEXTURE (sub_texture);
+    }
+
+  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))
+    {
+      g_error_free (error);
+      cogl_object_unref (fb);
+      return FALSE;
+    }
+
+  cogl_framebuffer_push_matrix (fb);
+  cogl_matrix_init_identity (&projection_matrix);
+  cogl_matrix_scale (&projection_matrix,
+                     1.0 / (priv->tex_width / 2.0),
+                     -1.0 / (priv->tex_height / 2.0), 0);
+  cogl_matrix_translate (&projection_matrix,
+                         -(priv->tex_width / 2.0),
+                         -(priv->tex_height / 2.0), 0);
+
+  cogl_framebuffer_set_projection_matrix (fb, &projection_matrix);
+
+  cogl_color_init_from_4ub (&clear_color, 0, 0, 0, 0);
+  cogl_framebuffer_clear (fb, COGL_BUFFER_BIT_COLOR, &clear_color);
+
+  do_paint (stex, fb, priv->texture, NULL);
+
+  cogl_framebuffer_pop_matrix (fb);
+
+  surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
+                                        clip->width, clip->height);
+  cogl_framebuffer_read_pixels (fb,
+                                clip->x, clip->y,
+                                clip->width, clip->height,
+                                CLUTTER_CAIRO_FORMAT_ARGB32,
+                                cairo_image_surface_get_data (surface));
+  cogl_object_unref (fb);
+
+  cairo_surface_mark_dirty (surface);
+
+  return surface;
+}
+
 /**
  * meta_shaped_texture_get_image:
  * @stex: A #MetaShapedTexture
@@ -927,7 +1043,6 @@ meta_shaped_texture_get_image (MetaShapedTexture     *stex,
   MetaShapedTexturePrivate *priv = stex->priv;
   cairo_rectangle_int_t *transformed_clip = NULL;
   CoglTexture *texture, *mask_texture;
-  cairo_rectangle_int_t texture_rect = { 0, 0, 0, 0 };
   cairo_surface_t *surface;
 
   g_return_val_if_fail (META_IS_SHAPED_TEXTURE (stex), NULL);
@@ -937,17 +1052,34 @@ meta_shaped_texture_get_image (MetaShapedTexture     *stex,
   if (texture == NULL)
     return NULL;
 
+  if (priv->tex_width == 0 || priv->tex_height == 0)
+    return NULL;
 
   if (clip != NULL)
     {
+      double tex_scale;
+      cairo_rectangle_int_t tex_rect;
+
       transformed_clip = alloca (sizeof (cairo_rectangle_int_t));
       *transformed_clip = *clip;
 
-      if (!meta_rectangle_intersect (&texture_rect, transformed_clip,
+      clutter_actor_get_scale (CLUTTER_ACTOR (stex), &tex_scale, NULL);
+      meta_rectangle_scale_double (transformed_clip, 1.0 / tex_scale,
+                                   META_ROUNDING_STRATEGY_GROW);
+
+      tex_rect = (cairo_rectangle_int_t) {
+        .width = priv->tex_width,
+        .height = priv->tex_height,
+      };
+
+      if (!meta_rectangle_intersect (&tex_rect, transformed_clip,
                                      transformed_clip))
         return NULL;
     }
 
+  if (should_get_via_offscreen (stex))
+    return get_image_via_offscreen (stex, transformed_clip);
+
   if (transformed_clip)
     texture = cogl_texture_new_from_sub_texture (texture,
                                                  transformed_clip->x,


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