texture: Add gdk_texture_diff()
authorBenjamin Otte <otte@redhat.com>
Thu, 27 Apr 2023 22:12:49 +0000 (00:12 +0200)
committerBenjamin Otte <otte@redhat.com>
Mon, 1 May 2023 20:24:14 +0000 (22:24 +0200)
... and use it in rendernodes.

Setting up textures for diffing is done via gdk_texture_set_diff() which
should only be used during texture construction.

Note that the pointers to next/previous are allowed to dangle if one of
the textures is finalized, but that's fine because we always check both
textures' links to each other before we consider the pointer valid.

gdk/gdktexture.c
gdk/gdktextureprivate.h
gsk/gskrendernodeimpl.c

index 41cb8ea11a16b24cff76e874816d098c8fa8991a..b392b90222be21ef5e77dcdb74a8ccf5913d0f68 100644 (file)
@@ -282,6 +282,8 @@ gdk_texture_dispose (GObject *object)
 {
   GdkTexture *self = GDK_TEXTURE (object);
 
+  g_clear_pointer (&self->diff_to_previous, cairo_region_destroy);
+
   gdk_texture_clear_render_data (self);
 
   G_OBJECT_CLASS (gdk_texture_parent_class)->dispose (object);
@@ -671,7 +673,47 @@ gdk_texture_do_download (GdkTexture      *texture,
                          guchar          *data,
                          gsize            stride)
 {
-  GDK_TEXTURE_GET_CLASS (texture)->download (texture, format, data,stride);
+  GDK_TEXTURE_GET_CLASS (texture)->download (texture, format, data, stride);
+}
+
+void
+gdk_texture_diff (GdkTexture     *self,
+                  GdkTexture     *other,
+                  cairo_region_t *region)
+{
+  if (self == other)
+    return;
+
+  if (self->previous_texture == other &&
+      g_atomic_pointer_get (&other->next_texture) == self)
+    {
+      cairo_region_union (region, self->diff_to_previous);
+    }
+  else if (other->previous_texture == self &&
+           g_atomic_pointer_get (&self->next_texture) == other)
+    {
+      cairo_region_union (region, other->diff_to_previous);
+    }
+  else
+    {
+      cairo_region_union_rectangle (region,
+                                    &(cairo_rectangle_int_t) {
+                                      0,
+                                      0,
+                                      MAX (self->width, other->width),
+                                      MAX (self->height, other->height)
+                                    });
+    }
+}
+
+void
+gdk_texture_set_diff (GdkTexture     *self,
+                      GdkTexture     *previous,
+                      cairo_region_t *diff)
+{
+  self->previous_texture = previous;
+  self->diff_to_previous = diff;
+  g_atomic_pointer_set (&previous->next_texture, self);
 }
 
 cairo_surface_t *
index fccf380d7cae8d713e3fe7032928e5ba62615fca..b0fc2a87852e52b2462f8024f10c84dc50b73ccf 100644 (file)
@@ -21,6 +21,12 @@ struct _GdkTexture
   gpointer render_key;
   gpointer render_data;
   GDestroyNotify render_notify;
+
+  /* for diffing swapchain-like textures.
+   * Links are only valid if both textures agree on them */
+  gpointer next_texture; /* atomic, no reference, may be invalid pointer */
+  gpointer previous_texture; /* no reference, may be invalid pointer */
+  cairo_region_t *diff_to_previous;
 };
 
 struct _GdkTextureClass {
@@ -42,6 +48,14 @@ void                    gdk_texture_do_download         (GdkTexture
                                                          GdkMemoryFormat         format,
                                                          guchar                 *data,
                                                          gsize                   stride);
+void                    gdk_texture_diff                (GdkTexture             *self,
+                                                         GdkTexture             *other,
+                                                         cairo_region_t         *region);
+
+void                    gdk_texture_set_diff            (GdkTexture             *self,
+                                                         GdkTexture             *previous,
+                                                         cairo_region_t         *diff);
+
 gboolean                gdk_texture_set_render_data     (GdkTexture             *self,
                                                          gpointer                key,
                                                          gpointer                data,
index ef31f459c7a0ee48e49a6a641c670a71d729ac5b..a0b6fd9b2fb02db415610568576d153066069648 100644 (file)
@@ -1650,12 +1650,28 @@ gsk_texture_node_diff (GskRenderNode  *node1,
 {
   GskTextureNode *self1 = (GskTextureNode *) node1;
   GskTextureNode *self2 = (GskTextureNode *) node2;
+  cairo_region_t *sub;
 
-  if (graphene_rect_equal (&node1->bounds, &node2->bounds) &&
-      self1->texture == self2->texture)
+  if (!graphene_rect_equal (&node1->bounds, &node2->bounds) ||
+      gdk_texture_get_width (self1->texture) != gdk_texture_get_width (self2->texture) ||
+      gdk_texture_get_height (self1->texture) != gdk_texture_get_height (self2->texture))
+    {
+      gsk_render_node_diff_impossible (node1, node2, region);
+      return;
+    }
+
+  if (self1->texture == self2->texture)
     return;
 
-  gsk_render_node_diff_impossible (node1, node2, region);
+  sub = cairo_region_create ();
+  gdk_texture_diff (self1->texture, self2->texture, sub);
+  region_union_region_affine (region,
+                              sub,
+                              node1->bounds.size.width / gdk_texture_get_width (self1->texture),
+                              node1->bounds.size.height / gdk_texture_get_height (self1->texture),
+                              node1->bounds.origin.x,
+                              node1->bounds.origin.y);
+  cairo_region_destroy (sub);
 }
 
 static void