diff --git a/docs/reference/gtk/running.md b/docs/reference/gtk/running.md
index 879e23d2e9..3ddae2cc36 100644
--- a/docs/reference/gtk/running.md
+++ b/docs/reference/gtk/running.md
@@ -217,6 +217,10 @@ A number of options affect behavior instead of logging:
`no-portals`
: Disable use of [portals](https://docs.flatpak.org/en/latest/portals.html)
+`force-offload`
+: Force graphics offload for all textures, even when slower. This allows
+ to debug offloading in the absence of dmabufs.
+
`gl-disable`
: Disable OpenGL support
diff --git a/gdk/gdk.c b/gdk/gdk.c
index 9787b437b8..95521522f2 100644
--- a/gdk/gdk.c
+++ b/gdk/gdk.c
@@ -122,6 +122,7 @@ static const GdkDebugKey gdk_debug_keys[] = {
{ "portals", GDK_DEBUG_PORTALS, "Force use of portals" },
{ "no-portals", GDK_DEBUG_NO_PORTALS, "Disable use of portals" },
+ { "force-offload", GDK_DEBUG_FORCE_OFFLOAD, "Force graphics offload for all textures" },
{ "gl-disable", GDK_DEBUG_GL_DISABLE, "Disable OpenGL support" },
{ "gl-no-fractional", GDK_DEBUG_GL_NO_FRACTIONAL, "Disable fractional scaling for OpenGL" },
{ "gl-debug", GDK_DEBUG_GL_DEBUG, "Insert debugging information in OpenGL" },
diff --git a/gdk/gdkdebugprivate.h b/gdk/gdkdebugprivate.h
index ba709fb5e9..359f13e6fa 100644
--- a/gdk/gdkdebugprivate.h
+++ b/gdk/gdkdebugprivate.h
@@ -44,7 +44,7 @@ typedef enum {
GDK_DEBUG_NO_PORTALS = 1 << 15,
GDK_DEBUG_GL_DISABLE = 1 << 16,
GDK_DEBUG_GL_NO_FRACTIONAL= 1 << 17,
-
+ GDK_DEBUG_FORCE_OFFLOAD = 1 << 18,
GDK_DEBUG_GL_DISABLE_GL = 1 << 19,
GDK_DEBUG_GL_DISABLE_GLES = 1 << 20,
GDK_DEBUG_GL_PREFER_GL = 1 << 21,
diff --git a/gdk/gdksubsurface.c b/gdk/gdksubsurface.c
index 5f0a26f62e..20d4810a4c 100644
--- a/gdk/gdksubsurface.c
+++ b/gdk/gdksubsurface.c
@@ -48,6 +48,14 @@ gdk_subsurface_class_init (GdkSubsurfaceClass *class)
object_class->finalize = gdk_subsurface_finalize;
}
+/*< private >
+ * gdk_subsurface_get_parent:
+ * @subsurface: a `GdkSubsurface`
+ *
+ * Returns the parent surface of @subsurface.
+ *
+ * Returns: the parent surface
+ */
GdkSurface *
gdk_subsurface_get_parent (GdkSubsurface *subsurface)
{
@@ -108,16 +116,41 @@ insert_subsurface (GdkSubsurface *subsurface,
}
}
+/*< private >
+ * gdk_subsurface_attach:
+ * @subsurface: the `GdkSubsurface`
+ * @texture: the texture to attach. This typically has to be a `GdkDmabufTexture`
+ * @source: the source rectangle (i.e. the subset of the texture) to display
+ * @dest: the dest rectangle, in application pixels, relative to the parent surface.
+ * It must be integral in application and device pixels, or attaching will fail
+ * @transform: the transform to apply to the texture contents before displaying
+ * @background: (nullable): the background rectangle, in application pixels relative
+ * to the parent surface. This tells GDK to put a black background of this
+ * size below the subsurface. It must be integral in application and device pixels,
+ * or attaching will fail
+ * @above: whether the subsurface should be above its sibling
+ * @sibling: (nullable): the sibling subsurface to stack relative to, or `NULL` to
+ * stack relative to the parent surface
+ *
+ * Attaches content to a subsurface.
+ *
+ * This function takes all the necessary arguments to determine the subsurface
+ * configuration, including its position, size, content, background and stacking.
+ *
+ * Returns: `TRUE` if the attaching succeeded
+ */
gboolean
gdk_subsurface_attach (GdkSubsurface *subsurface,
GdkTexture *texture,
const graphene_rect_t *source,
const graphene_rect_t *dest,
GdkTextureTransform transform,
+ const graphene_rect_t *background,
gboolean above,
GdkSubsurface *sibling)
{
GdkSurface *parent = subsurface->parent;
+ gboolean result;
g_return_val_if_fail (GDK_IS_SUBSURFACE (subsurface), FALSE);
g_return_val_if_fail (GDK_IS_TEXTURE (texture), FALSE);
@@ -131,6 +164,15 @@ gdk_subsurface_attach (GdkSubsurface *subsurface,
g_return_val_if_fail (sibling == NULL || GDK_IS_SUBSURFACE (sibling), FALSE);
g_return_val_if_fail (sibling == NULL || sibling->parent == subsurface->parent, FALSE);
+ result = GDK_SUBSURFACE_GET_CLASS (subsurface)->attach (subsurface,
+ texture,
+ source,
+ dest,
+ transform,
+ background,
+ above,
+ sibling);
+
remove_subsurface (subsurface);
if (sibling)
@@ -156,9 +198,17 @@ gdk_subsurface_attach (GdkSubsurface *subsurface,
}
}
- return GDK_SUBSURFACE_GET_CLASS (subsurface)->attach (subsurface, texture, source, dest, transform, above, sibling);
+ return result;
}
+/*< private >
+ * gdk_subsurface_detach:
+ * @subsurface: a `GdkSubsurface`
+ *
+ * Hides the subsurface.
+ *
+ * To show it again, you need to call gdk_subsurface_attach().
+ */
void
gdk_subsurface_detach (GdkSubsurface *subsurface)
{
@@ -169,6 +219,14 @@ gdk_subsurface_detach (GdkSubsurface *subsurface)
GDK_SUBSURFACE_GET_CLASS (subsurface)->detach (subsurface);
}
+/*< private >
+ * gdk_subsurface_get_texture:
+ * @subsurface: a `GdkSubsurface`
+ *
+ * Gets the texture that is currently displayed by the subsurface.
+ *
+ * Returns: (nullable): the texture that is displayed
+ */
GdkTexture *
gdk_subsurface_get_texture (GdkSubsurface *subsurface)
{
@@ -177,34 +235,91 @@ gdk_subsurface_get_texture (GdkSubsurface *subsurface)
return GDK_SUBSURFACE_GET_CLASS (subsurface)->get_texture (subsurface);
}
+/*< private >
+ * gdk_subsurface_get_source_rect:
+ * @subsurface: a `GdkSubsurface`
+ * @rect: (out caller-allocates): return location for the rectangle
+ *
+ * Returns the source rect that was specified in the most recent
+ * gdk_subsurface_attach() call for @subsurface.
+ */
void
-gdk_subsurface_get_source (GdkSubsurface *subsurface,
- graphene_rect_t *source)
+gdk_subsurface_get_source_rect (GdkSubsurface *subsurface,
+ graphene_rect_t *rect)
{
g_return_if_fail (GDK_IS_SUBSURFACE (subsurface));
- g_return_if_fail (source != NULL);
+ g_return_if_fail (rect != NULL);
- GDK_SUBSURFACE_GET_CLASS (subsurface)->get_source (subsurface, source);
+ GDK_SUBSURFACE_GET_CLASS (subsurface)->get_source_rect (subsurface, rect);
}
+/*< private >
+ * gdk_subsurface_get_texture_rect:
+ * @subsurface: a `GdkSubsurface`
+ * @rect: (out caller-allocates): return location for the rectangle
+ *
+ * Returns the texture rect that was specified in the most recent
+ * gdk_subsurface_attach() call for @subsurface.
+ */
void
-gdk_subsurface_get_dest (GdkSubsurface *subsurface,
- graphene_rect_t *dest)
+gdk_subsurface_get_texture_rect (GdkSubsurface *subsurface,
+ graphene_rect_t *rect)
{
g_return_if_fail (GDK_IS_SUBSURFACE (subsurface));
- g_return_if_fail (dest != NULL);
+ g_return_if_fail (rect != NULL);
- GDK_SUBSURFACE_GET_CLASS (subsurface)->get_dest (subsurface, dest);
+ GDK_SUBSURFACE_GET_CLASS (subsurface)->get_texture_rect (subsurface, rect);
}
+/*< private >
+ * gdk_subsurface_is_above_parent:
+ * @subsurface: a `GdkSubsurface`
+ *
+ * Returns whether the subsurface is above the parent surface
+ * or below. Note that a subsurface can be above its parent
+ * surface, and still be covered by sibling subsurfaces.
+ *
+ * Returns: `TRUE` if @subsurface is above its parent
+ */
gboolean
gdk_subsurface_is_above_parent (GdkSubsurface *subsurface)
{
- g_return_val_if_fail (GDK_IS_SUBSURFACE (subsurface), TRUE);
+ g_return_val_if_fail (GDK_IS_SUBSURFACE (subsurface), FALSE);
return subsurface->above_parent;
}
+/*< private>
+ * gdk_subsurface_get_sibling:
+ * @subsurface: the `GdkSubsurface`
+ * @above: whether to get the subsurface above
+ *
+ * Returns the subsurface above (or below) @subsurface in
+ * the stacking order.
+ *
+ * Returns: the sibling, or `NULL` if there is none.
+ */
+GdkSubsurface *
+gdk_subsurface_get_sibling (GdkSubsurface *subsurface,
+ gboolean above)
+{
+ g_return_val_if_fail (GDK_IS_SUBSURFACE (subsurface), NULL);
+
+ if (above)
+ return subsurface->sibling_above;
+ else
+ return subsurface->sibling_below;
+}
+
+/*< private >
+ * gdk_subsurface_get_transform:
+ * @subsurface: a `GdkSubsurface`
+ *
+ * Returns the transform that was specified in the most recent call to
+ * gdk_subsurface_attach() call for @subsurface.
+ *
+ * Returns: the transform
+ */
GdkTextureTransform
gdk_subsurface_get_transform (GdkSubsurface *subsurface)
{
@@ -213,3 +328,45 @@ gdk_subsurface_get_transform (GdkSubsurface *subsurface)
return GDK_SUBSURFACE_GET_CLASS (subsurface)->get_transform (subsurface);
}
+/*< private >
+ * gdk_subsurface_get_background_rect:
+ * @subsurface: a `GdkSubsurface`
+ * @rect: (out caller-allocates): return location for the rectangle
+ *
+ * Obtains the background rect that was specified in the most recent
+ * gdk_subsurface_attach() call for @subsurface.
+ *
+ * Returns: `TRUE` if @subsurface has a background
+ */
+gboolean
+gdk_subsurface_get_background_rect (GdkSubsurface *subsurface,
+ graphene_rect_t *rect)
+{
+ g_return_val_if_fail (GDK_IS_SUBSURFACE (subsurface), FALSE);
+ g_return_val_if_fail (rect != NULL, FALSE);
+
+ return GDK_SUBSURFACE_GET_CLASS (subsurface)->get_background_rect (subsurface, rect);
+}
+
+/*< private >
+ * gdk_subsurface_get_bounds:
+ * @subsurface: a `GdkSubsurface`
+ * @bounds: (out caller-allocates): return location for the bounds
+ *
+ * Returns the bounds of the subsurface.
+ *
+ * The bounds are the union of the texture and background rects.
+ */
+void
+gdk_subsurface_get_bounds (GdkSubsurface *subsurface,
+ graphene_rect_t *bounds)
+{
+ graphene_rect_t background;
+
+ g_return_if_fail (GDK_IS_SUBSURFACE (subsurface));
+ g_return_if_fail (bounds != NULL);
+
+ gdk_subsurface_get_texture_rect (subsurface, bounds);
+ if (gdk_subsurface_get_background_rect (subsurface, &background))
+ graphene_rect_union (bounds, &background, bounds);
+}
diff --git a/gdk/gdksubsurface.c.orig b/gdk/gdksubsurface.c.orig
new file mode 100644
index 0000000000..3c0e303977
--- /dev/null
+++ b/gdk/gdksubsurface.c.orig
@@ -0,0 +1,222 @@
+/* GDK - The GIMP Drawing Kit
+ * Copyright (C) 2023 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see .
+ */
+
+#include "config.h"
+
+#include "gdksubsurfaceprivate.h"
+#include "gdksurfaceprivate.h"
+#include "gdktexture.h"
+#include "gsk/gskrectprivate.h"
+
+G_DEFINE_TYPE (GdkSubsurface, gdk_subsurface, G_TYPE_OBJECT)
+
+static void
+gdk_subsurface_init (GdkSubsurface *self)
+{
+}
+
+static void
+gdk_subsurface_finalize (GObject *object)
+{
+ GdkSubsurface *subsurface = GDK_SUBSURFACE (object);
+
+ g_ptr_array_remove (subsurface->parent->subsurfaces, subsurface);
+ g_clear_object (&subsurface->parent);
+
+ G_OBJECT_CLASS (gdk_subsurface_parent_class)->finalize (object);
+}
+
+static void
+gdk_subsurface_class_init (GdkSubsurfaceClass *class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (class);
+
+ object_class->finalize = gdk_subsurface_finalize;
+}
+
+GdkSurface *
+gdk_subsurface_get_parent (GdkSubsurface *subsurface)
+{
+ g_return_val_if_fail (GDK_IS_SUBSURFACE (subsurface), NULL);
+
+ return subsurface->parent;
+}
+
+static void
+remove_subsurface (GdkSubsurface *subsurface)
+{
+ GdkSurface *parent = subsurface->parent;
+
+ if (parent->subsurfaces_above == subsurface)
+ parent->subsurfaces_above = subsurface->sibling_above;
+ if (parent->subsurfaces_below == subsurface)
+ parent->subsurfaces_below = subsurface->sibling_below;
+
+ if (subsurface->sibling_above)
+ subsurface->sibling_above->sibling_below = subsurface->sibling_below;
+ if (subsurface->sibling_below)
+ subsurface->sibling_below->sibling_above = subsurface->sibling_above;
+
+ subsurface->sibling_above = NULL;
+ subsurface->sibling_below = NULL;
+}
+
+static void
+insert_subsurface (GdkSubsurface *subsurface,
+ gboolean above,
+ GdkSubsurface *sibling)
+{
+ GdkSurface *parent = subsurface->parent;
+
+ subsurface->above_parent = sibling->above_parent;
+
+ if (above)
+ {
+ subsurface->sibling_above = sibling->sibling_above;
+ sibling->sibling_above = subsurface;
+ subsurface->sibling_below = sibling;
+ if (subsurface->sibling_above)
+ subsurface->sibling_above->sibling_below = subsurface;
+
+ if (parent->subsurfaces_below == sibling)
+ parent->subsurfaces_below = subsurface;
+ }
+ else
+ {
+ subsurface->sibling_below = sibling->sibling_below;
+ sibling->sibling_below = subsurface;
+ subsurface->sibling_above = sibling;
+ if (subsurface->sibling_below)
+ subsurface->sibling_below->sibling_above = subsurface;
+
+ if (parent->subsurfaces_above == sibling)
+ parent->subsurfaces_above = subsurface;
+ }
+}
+
+gboolean
+gdk_subsurface_attach (GdkSubsurface *subsurface,
+ GdkTexture *texture,
+ const graphene_rect_t *source,
+ const graphene_rect_t *dest,
+ GdkTextureTransform transform,
+ const graphene_rect_t *background,
+ gboolean above,
+ GdkSubsurface *sibling)
+{
+ GdkSurface *parent = subsurface->parent;
+
+ g_return_val_if_fail (GDK_IS_SUBSURFACE (subsurface), FALSE);
+ g_return_val_if_fail (GDK_IS_TEXTURE (texture), FALSE);
+ g_return_val_if_fail (source != NULL &&
+ gsk_rect_contains_rect (&GRAPHENE_RECT_INIT (0, 0,
+ gdk_texture_get_width (texture),
+ gdk_texture_get_height (texture)),
+ source), FALSE);
+ g_return_val_if_fail (dest != NULL, FALSE);
+ g_return_val_if_fail (sibling != subsurface, FALSE);
+ g_return_val_if_fail (sibling == NULL || GDK_IS_SUBSURFACE (sibling), FALSE);
+ g_return_val_if_fail (sibling == NULL || sibling->parent == subsurface->parent, FALSE);
+
+ remove_subsurface (subsurface);
+
+ if (sibling)
+ {
+ insert_subsurface (subsurface, above, sibling);
+ }
+ else
+ {
+ sibling = above ? parent->subsurfaces_above : parent->subsurfaces_below;
+
+ if (sibling)
+ {
+ insert_subsurface (subsurface, !above, sibling);
+ }
+ else
+ {
+ subsurface->above_parent = above;
+
+ if (above)
+ parent->subsurfaces_above = subsurface;
+ else
+ parent->subsurfaces_below = subsurface;
+ }
+ }
+
+ return GDK_SUBSURFACE_GET_CLASS (subsurface)->attach (subsurface,
+ texture,
+ source,
+ dest,
+ transform,
+ background,
+ above,
+ sibling);
+}
+
+void
+gdk_subsurface_detach (GdkSubsurface *subsurface)
+{
+ g_return_if_fail (GDK_IS_SUBSURFACE (subsurface));
+
+ remove_subsurface (subsurface);
+
+ GDK_SUBSURFACE_GET_CLASS (subsurface)->detach (subsurface);
+}
+
+GdkTexture *
+gdk_subsurface_get_texture (GdkSubsurface *subsurface)
+{
+ g_return_val_if_fail (GDK_IS_SUBSURFACE (subsurface), NULL);
+
+ return GDK_SUBSURFACE_GET_CLASS (subsurface)->get_texture (subsurface);
+}
+
+void
+gdk_subsurface_get_source (GdkSubsurface *subsurface,
+ graphene_rect_t *source)
+{
+ g_return_if_fail (GDK_IS_SUBSURFACE (subsurface));
+ g_return_if_fail (source != NULL);
+
+ GDK_SUBSURFACE_GET_CLASS (subsurface)->get_source (subsurface, source);
+}
+
+void
+gdk_subsurface_get_dest (GdkSubsurface *subsurface,
+ graphene_rect_t *dest)
+{
+ g_return_if_fail (GDK_IS_SUBSURFACE (subsurface));
+ g_return_if_fail (dest != NULL);
+
+ GDK_SUBSURFACE_GET_CLASS (subsurface)->get_dest (subsurface, dest);
+}
+
+gboolean
+gdk_subsurface_is_above_parent (GdkSubsurface *subsurface)
+{
+ g_return_val_if_fail (GDK_IS_SUBSURFACE (subsurface), TRUE);
+
+ return subsurface->above_parent;
+}
+
+GdkTextureTransform
+gdk_subsurface_get_transform (GdkSubsurface *subsurface)
+{
+ g_return_val_if_fail (GDK_IS_SUBSURFACE (subsurface), GDK_TEXTURE_TRANSFORM_NORMAL);
+
+ return GDK_SUBSURFACE_GET_CLASS (subsurface)->get_transform (subsurface);
+}
diff --git a/gdk/gdksubsurface.c.rej b/gdk/gdksubsurface.c.rej
new file mode 100644
index 0000000000..b68e718211
--- /dev/null
+++ b/gdk/gdksubsurface.c.rej
@@ -0,0 +1,16 @@
+--- gdk/gdksubsurface.c
++++ gdk/gdksubsurface.c
+@@ -213,3 +213,13 @@ gdk_subsurface_get_transform (GdkSubsurface *subsurface)
+ return GDK_SUBSURFACE_GET_CLASS (subsurface)->get_transform (subsurface);
+ }
+
++void
++gdk_subsurface_get_background (GdkSubsurface *subsurface,
++ graphene_rect_t *background)
++{
++ g_return_if_fail (GDK_IS_SUBSURFACE (subsurface));
++ g_return_if_fail (background != NULL);
++
++ GDK_SUBSURFACE_GET_CLASS (subsurface)->get_background (subsurface, background);
++}
++
diff --git a/gdk/gdksubsurfaceprivate.h b/gdk/gdksubsurfaceprivate.h
index ea36af1bf5..6a28ac3f1d 100644
--- a/gdk/gdksubsurfaceprivate.h
+++ b/gdk/gdksubsurfaceprivate.h
@@ -62,44 +62,53 @@ struct _GdkSubsurfaceClass
{
GObjectClass parent_class;
- gboolean (* attach) (GdkSubsurface *subsurface,
- GdkTexture *texture,
- const graphene_rect_t *source,
- const graphene_rect_t *dest,
- GdkTextureTransform transform,
- gboolean above,
- GdkSubsurface *sibling);
- void (* detach) (GdkSubsurface *subsurface);
- GdkTexture * (* get_texture) (GdkSubsurface *subsurface);
- void (* get_source) (GdkSubsurface *subsurface,
- graphene_rect_t *source);
- void (* get_dest) (GdkSubsurface *subsurface,
- graphene_rect_t *dest);
+ gboolean (* attach) (GdkSubsurface *subsurface,
+ GdkTexture *texture,
+ const graphene_rect_t *source,
+ const graphene_rect_t *dest,
+ GdkTextureTransform transform,
+ const graphene_rect_t *bg,
+ gboolean above,
+ GdkSubsurface *sibling);
+ void (* detach) (GdkSubsurface *subsurface);
+ GdkTexture * (* get_texture) (GdkSubsurface *subsurface);
+ void (* get_source_rect) (GdkSubsurface *subsurface,
+ graphene_rect_t *rect);
+ void (* get_texture_rect) (GdkSubsurface *subsurface,
+ graphene_rect_t *rect);
GdkTextureTransform
- (* get_transform) (GdkSubsurface *subsurface);
+ (* get_transform) (GdkSubsurface *subsurface);
+ gboolean (* get_background_rect) (GdkSubsurface *subsurface,
+ graphene_rect_t *rect);
};
-GType gdk_subsurface_get_type (void) G_GNUC_CONST;
+GType gdk_subsurface_get_type (void) G_GNUC_CONST;
-GdkSurface * gdk_subsurface_get_parent (GdkSubsurface *subsurface);
+GdkSurface * gdk_subsurface_get_parent (GdkSubsurface *subsurface);
-gboolean gdk_subsurface_attach (GdkSubsurface *subsurface,
- GdkTexture *texture,
- const graphene_rect_t *source,
- const graphene_rect_t *dest,
- GdkTextureTransform transform,
- gboolean above,
- GdkSubsurface *sibling);
-void gdk_subsurface_detach (GdkSubsurface *subsurface);
-GdkTexture * gdk_subsurface_get_texture (GdkSubsurface *subsurface);
-void gdk_subsurface_get_source (GdkSubsurface *subsurface,
- graphene_rect_t *source);
-void gdk_subsurface_get_dest (GdkSubsurface *subsurface,
- graphene_rect_t *dest);
-gboolean gdk_subsurface_is_above_parent (GdkSubsurface *subsurface);
+gboolean gdk_subsurface_attach (GdkSubsurface *subsurface,
+ GdkTexture *texture,
+ const graphene_rect_t *source,
+ const graphene_rect_t *dest,
+ GdkTextureTransform transform,
+ const graphene_rect_t *background,
+ gboolean above,
+ GdkSubsurface *sibling);
+void gdk_subsurface_detach (GdkSubsurface *subsurface);
+GdkTexture * gdk_subsurface_get_texture (GdkSubsurface *subsurface);
+void gdk_subsurface_get_source_rect (GdkSubsurface *subsurface,
+ graphene_rect_t *rect);
+void gdk_subsurface_get_texture_rect (GdkSubsurface *subsurface,
+ graphene_rect_t *rect);
+gboolean gdk_subsurface_is_above_parent (GdkSubsurface *subsurface);
+GdkSubsurface * gdk_subsurface_get_sibling (GdkSubsurface *subsurface,
+ gboolean above);
GdkTextureTransform
- gdk_subsurface_get_transform (GdkSubsurface *subsurface);
-
+ gdk_subsurface_get_transform (GdkSubsurface *subsurface);
+gboolean gdk_subsurface_get_background_rect (GdkSubsurface *subsurface,
+ graphene_rect_t *rect);
+void gdk_subsurface_get_bounds (GdkSubsurface *subsurface,
+ graphene_rect_t *bounds);
G_END_DECLS
diff --git a/gdk/gdksubsurfaceprivate.h.orig b/gdk/gdksubsurfaceprivate.h.orig
new file mode 100644
index 0000000000..ad4204f542
--- /dev/null
+++ b/gdk/gdksubsurfaceprivate.h.orig
@@ -0,0 +1,107 @@
+/* GDK - The GIMP Drawing Kit
+ * Copyright (C) 2023 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see .
+ */
+
+/* Uninstalled header defining types and functions internal to GDK */
+
+#pragma once
+
+#include "gdkenumtypes.h"
+#include "gdksurface.h"
+#include
+
+G_BEGIN_DECLS
+
+typedef struct _GdkSubsurface GdkSubsurface;
+typedef struct _GdkSubsurfaceClass GdkSubsurfaceClass;
+
+#define GDK_TYPE_SUBSURFACE (gdk_subsurface_get_type ())
+#define GDK_SUBSURFACE(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), GDK_TYPE_SUBSURFACE, GdkSubsurface))
+#define GDK_SUBSURFACE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDK_TYPE_SUBSURFACE, GdkSubsurfaceClass))
+#define GDK_IS_SUBSURFACE(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), GDK_TYPE_SUBSURFACE))
+#define GDK_SUBSURFACE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_SUBSURFACE, GdkSubsurfaceClass))
+
+struct _GdkSubsurface
+{
+ GObject parent_instance;
+
+ GdkSurface *parent;
+
+ int ref_count;
+
+ gboolean above_parent;
+ GdkSubsurface *sibling_above;
+ GdkSubsurface *sibling_below;
+};
+
+typedef enum {
+ GDK_TEXTURE_TRANSFORM_NORMAL,
+ GDK_TEXTURE_TRANSFORM_90,
+ GDK_TEXTURE_TRANSFORM_180,
+ GDK_TEXTURE_TRANSFORM_270,
+ GDK_TEXTURE_TRANSFORM_FLIPPED,
+ GDK_TEXTURE_TRANSFORM_FLIPPED_90,
+ GDK_TEXTURE_TRANSFORM_FLIPPED_180,
+ GDK_TEXTURE_TRANSFORM_FLIPPED_270,
+} GdkTextureTransform;
+
+struct _GdkSubsurfaceClass
+{
+ GObjectClass parent_class;
+
+ gboolean (* attach) (GdkSubsurface *subsurface,
+ GdkTexture *texture,
+ const graphene_rect_t *source,
+ const graphene_rect_t *dest,
+ GdkTextureTransform transform,
+ const graphene_rect_t *bg,
+ gboolean above,
+ GdkSubsurface *sibling);
+ void (* detach) (GdkSubsurface *subsurface);
+ GdkTexture * (* get_texture) (GdkSubsurface *subsurface);
+ void (* get_source) (GdkSubsurface *subsurface,
+ graphene_rect_t *source);
+ void (* get_dest) (GdkSubsurface *subsurface,
+ graphene_rect_t *dest);
+ GdkTextureTransform
+ (* get_transform) (GdkSubsurface *subsurface);
+};
+
+GType gdk_subsurface_get_type (void) G_GNUC_CONST;
+
+GdkSurface * gdk_subsurface_get_parent (GdkSubsurface *subsurface);
+
+gboolean gdk_subsurface_attach (GdkSubsurface *subsurface,
+ GdkTexture *texture,
+ const graphene_rect_t *source,
+ const graphene_rect_t *dest,
+ GdkTextureTransform transform,
+ const graphene_rect_t *background,
+ gboolean above,
+ GdkSubsurface *sibling);
+void gdk_subsurface_detach (GdkSubsurface *subsurface);
+GdkTexture * gdk_subsurface_get_texture (GdkSubsurface *subsurface);
+void gdk_subsurface_get_source (GdkSubsurface *subsurface,
+ graphene_rect_t *source);
+void gdk_subsurface_get_dest (GdkSubsurface *subsurface,
+ graphene_rect_t *dest);
+gboolean gdk_subsurface_is_above_parent (GdkSubsurface *subsurface);
+GdkTextureTransform
+ gdk_subsurface_get_transform (GdkSubsurface *subsurface);
+
+
+G_END_DECLS
+
diff --git a/gdk/wayland/gdkdisplay-wayland.c b/gdk/wayland/gdkdisplay-wayland.c
index cc2c51dfa6..46af453bd3 100644
--- a/gdk/wayland/gdkdisplay-wayland.c
+++ b/gdk/wayland/gdkdisplay-wayland.c
@@ -606,7 +606,13 @@ gdk_registry_handle_global (void *data,
&wp_presentation_interface,
MIN (version, 1));
}
-
+ else if (strcmp (interface, wp_single_pixel_buffer_manager_v1_interface.name) == 0)
+ {
+ display_wayland->single_pixel_buffer =
+ wl_registry_bind (display_wayland->wl_registry, id,
+ &wp_single_pixel_buffer_manager_v1_interface,
+ MIN (version, 1));
+ }
g_hash_table_insert (display_wayland->known_globals,
GUINT_TO_POINTER (id), g_strdup (interface));
@@ -817,6 +823,7 @@ gdk_wayland_display_dispose (GObject *object)
g_clear_pointer (&display_wayland->fractional_scale, wp_fractional_scale_manager_v1_destroy);
g_clear_pointer (&display_wayland->viewporter, wp_viewporter_destroy);
g_clear_pointer (&display_wayland->presentation, wp_presentation_destroy);
+ g_clear_pointer (&display_wayland->single_pixel_buffer, wp_single_pixel_buffer_manager_v1_destroy);
g_clear_pointer (&display_wayland->linux_dmabuf, zwp_linux_dmabuf_v1_destroy);
g_clear_pointer (&display_wayland->linux_dmabuf_feedback, zwp_linux_dmabuf_feedback_v1_destroy);
if (display_wayland->linux_dmabuf_formats)
diff --git a/gdk/wayland/gdkdisplay-wayland.h b/gdk/wayland/gdkdisplay-wayland.h
index f08323ee3e..b2a5f84509 100644
--- a/gdk/wayland/gdkdisplay-wayland.h
+++ b/gdk/wayland/gdkdisplay-wayland.h
@@ -40,6 +40,7 @@
#include
#include
#include
+#include
#include
#include
@@ -127,6 +128,7 @@ struct _GdkWaylandDisplay
struct wp_fractional_scale_manager_v1 *fractional_scale;
struct wp_viewporter *viewporter;
struct wp_presentation *presentation;
+ struct wp_single_pixel_buffer_manager_v1 *single_pixel_buffer;
GList *async_roundtrips;
diff --git a/gdk/wayland/gdksubsurface-wayland-private.h b/gdk/wayland/gdksubsurface-wayland-private.h
index fb5c98a85a..9b8378ccc2 100644
--- a/gdk/wayland/gdksubsurface-wayland-private.h
+++ b/gdk/wayland/gdksubsurface-wayland-private.h
@@ -29,6 +29,12 @@ struct _GdkWaylandSubsurface
struct wl_region *opaque_region;
struct wl_callback *frame_callback;
+
+ struct wl_surface *bg_surface;
+ struct wl_subsurface *bg_subsurface;
+ struct wp_viewport *bg_viewport;
+ cairo_rectangle_int_t bg_rect;
+ gboolean bg_attached;
};
struct _GdkWaylandSubsurfaceClass
diff --git a/gdk/wayland/gdksubsurface-wayland.c b/gdk/wayland/gdksubsurface-wayland.c
index cecd10cb28..eaf5b72e8b 100644
--- a/gdk/wayland/gdksubsurface-wayland.c
+++ b/gdk/wayland/gdksubsurface-wayland.c
@@ -25,6 +25,8 @@
#include "gdkdmabuftextureprivate.h"
#include "gdksurface-wayland-private.h"
#include "gdksubsurfaceprivate.h"
+#include "gdkdebugprivate.h"
+#include "gsk/gskrectprivate.h"
#include "linux-dmabuf-unstable-v1-client-protocol.h"
@@ -46,10 +48,55 @@ gdk_wayland_subsurface_finalize (GObject *object)
g_clear_pointer (&self->viewport, wp_viewport_destroy);
g_clear_pointer (&self->subsurface, wl_subsurface_destroy);
g_clear_pointer (&self->surface, wl_surface_destroy);
+ g_clear_pointer (&self->bg_viewport, wp_viewport_destroy);
+ g_clear_pointer (&self->bg_subsurface, wl_subsurface_destroy);
+ g_clear_pointer (&self->bg_surface, wl_surface_destroy);
G_OBJECT_CLASS (gdk_wayland_subsurface_parent_class)->finalize (object);
}
+static void
+shm_buffer_release (void *data,
+ struct wl_buffer *buffer)
+{
+ cairo_surface_t *surface = data;
+
+ /* Note: the wl_buffer is destroyed as cairo user data */
+ cairo_surface_destroy (surface);
+}
+
+static const struct wl_buffer_listener shm_buffer_listener = {
+ shm_buffer_release,
+};
+
+static struct wl_buffer *
+get_shm_wl_buffer (GdkWaylandSubsurface *self,
+ GdkTexture *texture)
+{
+ GdkWaylandDisplay *display = GDK_WAYLAND_DISPLAY (gdk_surface_get_display (GDK_SUBSURFACE (self)->parent));
+ int width, height;
+ cairo_surface_t *surface;
+ GdkTextureDownloader *downloader;
+ struct wl_buffer *buffer;
+
+ width = gdk_texture_get_width (texture);
+ height = gdk_texture_get_height (texture);
+ surface = gdk_wayland_display_create_shm_surface (display, width, height, &GDK_FRACTIONAL_SCALE_INIT_INT (1));
+
+ downloader = gdk_texture_downloader_new (texture);
+
+ gdk_texture_downloader_download_into (downloader,
+ cairo_image_surface_get_data (surface),
+ cairo_image_surface_get_stride (surface));
+
+ gdk_texture_downloader_free (downloader);
+
+ buffer = _gdk_wayland_shm_surface_get_wl_buffer (surface);
+ wl_buffer_add_listener (buffer, &shm_buffer_listener, surface);
+
+ return buffer;
+}
+
static void
dmabuf_buffer_release (void *data,
struct wl_buffer *buffer)
@@ -96,8 +143,8 @@ static const struct zwp_linux_buffer_params_v1_listener params_listener = {
};
static struct wl_buffer *
-get_wl_buffer (GdkWaylandSubsurface *self,
- GdkTexture *texture)
+get_dmabuf_wl_buffer (GdkWaylandSubsurface *self,
+ GdkTexture *texture)
{
GdkWaylandDisplay *display = GDK_WAYLAND_DISPLAY (gdk_surface_get_display (GDK_SUBSURFACE (self)->parent));
const GdkDmabuf *dmabuf;
@@ -148,6 +195,52 @@ get_wl_buffer (GdkWaylandSubsurface *self,
return buffer;
}
+static struct wl_buffer *
+get_wl_buffer (GdkWaylandSubsurface *self,
+ GdkTexture *texture)
+{
+ GdkDisplay *display = gdk_surface_get_display (GDK_SUBSURFACE (self)->parent);
+ struct wl_buffer *buffer = NULL;
+
+ if (GDK_IS_DMABUF_TEXTURE (texture))
+ buffer = get_dmabuf_wl_buffer (self, texture);
+
+ if (GDK_DISPLAY_DEBUG_CHECK (display, FORCE_OFFLOAD))
+ {
+ if (!buffer)
+ buffer = get_shm_wl_buffer (self, texture);
+ }
+
+ return buffer;
+}
+
+static void
+sp_buffer_release (void *data,
+ struct wl_buffer *buffer)
+{
+ wl_buffer_destroy (buffer);
+}
+
+static const struct wl_buffer_listener sp_buffer_listener = {
+ sp_buffer_release,
+};
+
+static struct wl_buffer *
+get_sp_buffer (GdkWaylandSubsurface *self)
+{
+ GdkWaylandDisplay *display = GDK_WAYLAND_DISPLAY (gdk_surface_get_display (GDK_SUBSURFACE (self)->parent));
+ struct wl_buffer *buffer = NULL;
+
+ if (display->single_pixel_buffer)
+ buffer = wp_single_pixel_buffer_manager_v1_create_u32_rgba_buffer (display->single_pixel_buffer,
+ 0, 0, 0, 0xffffffffU);
+
+ if (buffer)
+ wl_buffer_add_listener (buffer, &sp_buffer_listener, self);
+
+ return buffer;
+}
+
static inline enum wl_output_transform
gdk_texture_transform_to_wl (GdkTextureTransform transform)
{
@@ -160,24 +253,80 @@ wl_output_transform_to_gdk (enum wl_output_transform transform)
return (GdkTextureTransform) transform;
}
+static void
+ensure_bg_surface (GdkWaylandSubsurface *self)
+{
+ GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (GDK_SUBSURFACE (self)->parent);
+ GdkDisplay *display = gdk_surface_get_display (GDK_SUBSURFACE (self)->parent);
+ GdkWaylandDisplay *disp = GDK_WAYLAND_DISPLAY (display);
+ struct wl_region *region;
+
+ if (self->bg_surface)
+ return;
+
+ self->bg_surface = wl_compositor_create_surface (disp->compositor);
+ self->bg_subsurface = wl_subcompositor_get_subsurface (disp->subcompositor,
+ self->bg_surface,
+ impl->display_server.wl_surface);
+ self->bg_viewport = wp_viewporter_get_viewport (disp->viewporter, self->bg_surface);
+
+ /* We are opaque */
+ wl_surface_set_opaque_region (self->bg_surface, self->opaque_region);
+
+ /* No input, please */
+ region = wl_compositor_create_region (disp->compositor);
+ wl_surface_set_input_region (self->bg_surface, region);
+ wl_region_destroy (region);
+}
+
+static inline gboolean
+scaled_rect_is_integral (const graphene_rect_t *rect,
+ float scale,
+ graphene_rect_t *device_rect)
+{
+ cairo_rectangle_int_t device_int;
+
+ gsk_rect_scale (rect, scale, scale, device_rect);
+
+ device_int.x = device_rect->origin.x;
+ device_int.y = device_rect->origin.y;
+ device_int.width = device_rect->size.width;
+ device_int.height = device_rect->size.height;
+
+ return device_int.x == device_rect->origin.x &&
+ device_int.y == device_rect->origin.y &&
+ device_int.width == device_rect->size.width &&
+ device_int.height == device_rect->size.height;
+}
+
static gboolean
gdk_wayland_subsurface_attach (GdkSubsurface *sub,
GdkTexture *texture,
const graphene_rect_t *source,
const graphene_rect_t *dest,
GdkTextureTransform transform,
+ const graphene_rect_t *background,
gboolean above,
GdkSubsurface *sibling)
{
GdkWaylandSubsurface *self = GDK_WAYLAND_SUBSURFACE (sub);
GdkWaylandSurface *parent = GDK_WAYLAND_SURFACE (sub->parent);
+ GdkWaylandDisplay *display = GDK_WAYLAND_DISPLAY (gdk_surface_get_display (sub->parent));
struct wl_buffer *buffer = NULL;
gboolean result = FALSE;
GdkWaylandSubsurface *sib = sibling ? GDK_WAYLAND_SUBSURFACE (sibling) : NULL;
gboolean will_be_above;
double scale;
graphene_rect_t device_rect;
- cairo_rectangle_int_t device_dest;
+ gboolean has_background;
+ enum wl_output_transform tf;
+ gboolean dest_changed = FALSE;
+ gboolean source_changed = FALSE;
+ gboolean transform_changed = FALSE;
+ gboolean stacking_changed = FALSE;
+ gboolean needs_commit = FALSE;
+ gboolean background_changed = FALSE;
+ gboolean needs_bg_commit = FALSE;
if (sibling)
will_be_above = sibling->above_parent;
@@ -190,29 +339,74 @@ gdk_wayland_subsurface_attach (GdkSubsurface *sub,
return FALSE;
}
- self->dest.x = dest->origin.x;
- self->dest.y = dest->origin.y;
- self->dest.width = dest->size.width;
- self->dest.height = dest->size.height;
+ if (self->dest.x != dest->origin.x ||
+ self->dest.y != dest->origin.y ||
+ self->dest.width != dest->size.width ||
+ self->dest.height != dest->size.height)
+ {
+ self->dest.x = dest->origin.x;
+ self->dest.y = dest->origin.y;
+ self->dest.width = dest->size.width;
+ self->dest.height = dest->size.height;
+ dest_changed = TRUE;
+ }
- self->source.origin.x = source->origin.x;
- self->source.origin.y = source->origin.y;
- self->source.size.width = source->size.width;
- self->source.size.height = source->size.height;
+ if (!gsk_rect_equal (&self->source, source))
+ {
+ self->source.origin.x = source->origin.x;
+ self->source.origin.y = source->origin.y;
+ self->source.size.width = source->size.width;
+ self->source.size.height = source->size.height;
+ source_changed = TRUE;
+ }
- self->transform = gdk_texture_transform_to_wl (transform);
+ tf = gdk_texture_transform_to_wl (transform);
+ if (self->transform != tf)
+ {
+ self->transform = tf;
+ transform_changed = TRUE;
+ }
+
+ if (sibling != gdk_subsurface_get_sibling (sub, above) ||
+ will_be_above != gdk_subsurface_is_above_parent (sub))
+ stacking_changed = TRUE;
+
+ if (self->texture == NULL)
+ {
+ dest_changed = TRUE;
+ source_changed = TRUE;
+ transform_changed = TRUE;
+ stacking_changed = TRUE;
+ }
scale = gdk_fractional_scale_to_double (&parent->scale);
- device_rect.origin.x = dest->origin.x * scale;
- device_rect.origin.y = dest->origin.y * scale;
- device_rect.size.width = dest->size.width * scale;
- device_rect.size.height = dest->size.height * scale;
+ if (background)
+ {
+ background_changed =
+ !self->bg_attached ||
+ self->bg_rect.x != background->origin.x ||
+ self->bg_rect.y != background->origin.y ||
+ self->bg_rect.width != background->size.width ||
+ self->bg_rect.height != background->size.height;
+ self->bg_rect.x = background->origin.x;
+ self->bg_rect.y = background->origin.y;
+ self->bg_rect.width = background->size.width;
+ self->bg_rect.height = background->size.height;
+ }
+ else
+ {
+ background_changed = self->bg_attached;
+ self->bg_rect.x = 0;
+ self->bg_rect.y = 0;
+ self->bg_rect.width = 0;
+ self->bg_rect.height = 0;
+ }
- device_dest.x = device_rect.origin.x;
- device_dest.y = device_rect.origin.y;
- device_dest.width = device_rect.size.width;
- device_dest.height = device_rect.size.height;
+ has_background = self->bg_rect.width > 0 && self->bg_rect.height > 0;
+
+ if (has_background)
+ ensure_bg_surface (self);
if (self->dest.x != dest->origin.x ||
self->dest.y != dest->origin.y ||
@@ -227,19 +421,26 @@ gdk_wayland_subsurface_attach (GdkSubsurface *sub,
gdk_texture_get_height (texture),
self);
}
- else if (device_dest.x != device_rect.origin.x ||
- device_dest.y != device_rect.origin.y ||
- device_dest.width != device_rect.size.width ||
- device_dest.height != device_rect.size.height)
+ else if (!scaled_rect_is_integral (dest, scale, &device_rect))
{
GDK_DISPLAY_DEBUG (gdk_surface_get_display (sub->parent), OFFLOAD,
"Non-integral device coordinates %g %g %g %g (fractional scale %.2f), hiding subsurface %p",
device_rect.origin.x, device_rect.origin.y,
- device_rect.size.width, device_rect.size.width,
+ device_rect.size.width, device_rect.size.height,
scale,
self);
}
- else if (!GDK_IS_DMABUF_TEXTURE (texture))
+ else if (background && !scaled_rect_is_integral (background, scale, &device_rect))
+ {
+ GDK_DISPLAY_DEBUG (gdk_surface_get_display (sub->parent), OFFLOAD,
+ "Non-integral background device coordinates %g %g %g %g (fractional scale %.2f), hiding background of subsurface %p",
+ device_rect.origin.x, device_rect.origin.y,
+ device_rect.size.width, device_rect.size.height,
+ scale,
+ self);
+ }
+ else if (!GDK_IS_DMABUF_TEXTURE (texture) &&
+ !GDK_DISPLAY_DEBUG_CHECK (gdk_surface_get_display (sub->parent), FORCE_OFFLOAD))
{
GDK_DISPLAY_DEBUG (gdk_surface_get_display (sub->parent), OFFLOAD,
"%dx%d %s is not a GdkDmabufTexture, hiding subsurface %p",
@@ -257,6 +458,12 @@ gdk_wayland_subsurface_attach (GdkSubsurface *sub,
gdk_texture_get_height (texture),
self);
}
+ else if (has_background && !display->single_pixel_buffer)
+ {
+ GDK_DISPLAY_DEBUG (gdk_surface_get_display (sub->parent), OFFLOAD,
+ "Cannot offload subsurface %p with background, no single-pixel buffer support",
+ self);
+ }
else
{
gboolean was_transparent;
@@ -316,14 +523,28 @@ gdk_wayland_subsurface_attach (GdkSubsurface *sub,
if (result)
{
- wl_surface_set_buffer_transform (self->surface, self->transform);
- wl_subsurface_set_position (self->subsurface, self->dest.x, self->dest.y);
- wp_viewport_set_destination (self->viewport, self->dest.width, self->dest.height);
- wp_viewport_set_source (self->viewport,
- wl_fixed_from_double (self->source.origin.x),
- wl_fixed_from_double (self->source.origin.y),
- wl_fixed_from_double (self->source.size.width),
- wl_fixed_from_double (self->source.size.height));
+ if (transform_changed)
+ {
+ wl_surface_set_buffer_transform (self->surface, self->transform);
+ needs_commit = TRUE;
+ }
+
+ if (dest_changed)
+ {
+ wl_subsurface_set_position (self->subsurface, self->dest.x, self->dest.y);
+ wp_viewport_set_destination (self->viewport, self->dest.width, self->dest.height);
+ needs_commit = TRUE;
+ }
+
+ if (source_changed)
+ {
+ wp_viewport_set_source (self->viewport,
+ wl_fixed_from_double (self->source.origin.x),
+ wl_fixed_from_double (self->source.origin.y),
+ wl_fixed_from_double (self->source.size.width),
+ wl_fixed_from_double (self->source.size.height));
+ needs_commit = TRUE;
+ }
if (buffer)
{
@@ -332,39 +553,95 @@ gdk_wayland_subsurface_attach (GdkSubsurface *sub,
0, 0,
gdk_texture_get_width (texture),
gdk_texture_get_height (texture));
+ needs_commit = TRUE;
+ }
+ if (has_background)
+ {
+ if (background_changed)
+ {
+ wl_subsurface_set_position (self->bg_subsurface, self->bg_rect.x, self->bg_rect.y);
+ wp_viewport_set_destination (self->bg_viewport, self->bg_rect.width, self->bg_rect.height);
+ needs_bg_commit = TRUE;
+ }
+
+ if (!self->bg_attached)
+ {
+ self->bg_attached = TRUE;
+
+ wp_viewport_set_source (self->bg_viewport,
+ wl_fixed_from_int (0),
+ wl_fixed_from_int (0),
+ wl_fixed_from_int (1),
+ wl_fixed_from_int (1));
+ wl_surface_attach (self->bg_surface, get_sp_buffer (self), 0, 0);
+ wl_surface_damage_buffer (self->bg_surface, 0, 0, 1, 1);
+ needs_bg_commit = TRUE;
+ }
+ }
+ else
+ {
+ if (self->bg_attached)
+ {
+ self->bg_attached = FALSE;
+ wl_surface_attach (self->bg_surface, NULL, 0, 0);
+ needs_bg_commit = TRUE;
+ }
}
result = TRUE;
}
else
{
- g_set_object (&self->texture, NULL);
+ if (g_set_object (&self->texture, NULL))
+ {
+ wl_surface_attach (self->surface, NULL, 0, 0);
+ needs_commit = TRUE;
+ }
- wl_surface_attach (self->surface, NULL, 0, 0);
+ if (self->bg_attached)
+ {
+ self->bg_attached = FALSE;
+ wl_surface_attach (self->bg_surface, NULL, 0, 0);
+ needs_bg_commit = TRUE;
+ }
}
- if (sib)
+ if (stacking_changed)
{
- if (above)
- wl_subsurface_place_above (self->subsurface, sib->surface);
+ if (sib)
+ {
+ if (above)
+ wl_subsurface_place_above (self->subsurface, sib->surface);
+ else
+ wl_subsurface_place_below (self->subsurface, sib->surface);
+ }
else
- wl_subsurface_place_below (self->subsurface, sib->surface);
- }
- else
- {
- if (above)
- wl_subsurface_place_above (self->subsurface,
- GDK_WAYLAND_SURFACE (sub->parent)->display_server.wl_surface);
- else
- wl_subsurface_place_below (self->subsurface,
- GDK_WAYLAND_SURFACE (sub->parent)->display_server.wl_surface);
+ {
+ if (above)
+ wl_subsurface_place_above (self->subsurface,
+ GDK_WAYLAND_SURFACE (sub->parent)->display_server.wl_surface);
+ else
+ wl_subsurface_place_below (self->subsurface,
+ GDK_WAYLAND_SURFACE (sub->parent)->display_server.wl_surface);
+ }
+ needs_commit = TRUE;
+
+ if (self->bg_attached)
+ {
+ wl_subsurface_place_below (self->bg_subsurface, self->surface);
+ needs_bg_commit = TRUE;
+ }
}
- wl_surface_commit (self->surface);
+ if (needs_commit)
+ wl_surface_commit (self->surface);
- ((GdkWaylandSurface *)sub->parent)->has_pending_subsurface_commits = TRUE;
- GDK_WAYLAND_SURFACE (sub->parent)->opaque_region_dirty = TRUE;
+ if (needs_bg_commit)
+ wl_surface_commit (self->bg_surface);
+
+ ((GdkWaylandSurface *)sub->parent)->has_pending_subsurface_commits = needs_commit || needs_bg_commit;
+ GDK_WAYLAND_SURFACE (sub->parent)->opaque_region_dirty = stacking_changed || dest_changed || background_changed;
return result;
}
@@ -385,6 +662,13 @@ gdk_wayland_subsurface_detach (GdkSubsurface *sub)
wl_surface_set_opaque_region (self->surface, self->opaque_region);
wl_surface_commit (self->surface);
+ if (self->bg_attached)
+ {
+ wl_surface_attach (self->bg_surface, NULL, 0, 0);
+ wl_surface_commit (self->bg_surface);
+ self->bg_attached = FALSE;
+ }
+
((GdkWaylandSurface *)sub->parent)->has_pending_subsurface_commits = TRUE;
GDK_WAYLAND_SURFACE (sub->parent)->opaque_region_dirty = TRUE;
}
@@ -398,27 +682,27 @@ gdk_wayland_subsurface_get_texture (GdkSubsurface *sub)
}
static void
-gdk_wayland_subsurface_get_dest (GdkSubsurface *sub,
- graphene_rect_t *dest)
+gdk_wayland_subsurface_get_texture_rect (GdkSubsurface *sub,
+ graphene_rect_t *rect)
{
GdkWaylandSubsurface *self = GDK_WAYLAND_SUBSURFACE (sub);
- dest->origin.x = self->dest.x;
- dest->origin.y = self->dest.y;
- dest->size.width = self->dest.width;
- dest->size.height = self->dest.height;
+ rect->origin.x = self->dest.x;
+ rect->origin.y = self->dest.y;
+ rect->size.width = self->dest.width;
+ rect->size.height = self->dest.height;
}
static void
-gdk_wayland_subsurface_get_source (GdkSubsurface *sub,
- graphene_rect_t *source)
+gdk_wayland_subsurface_get_source_rect (GdkSubsurface *sub,
+ graphene_rect_t *rect)
{
GdkWaylandSubsurface *self = GDK_WAYLAND_SUBSURFACE (sub);
- source->origin.x = self->source.origin.x;
- source->origin.y = self->source.origin.y;
- source->size.width = self->source.size.width;
- source->size.height = self->source.size.height;
+ rect->origin.x = self->source.origin.x;
+ rect->origin.y = self->source.origin.y;
+ rect->size.width = self->source.size.width;
+ rect->size.height = self->source.size.height;
}
static GdkTextureTransform
@@ -429,6 +713,20 @@ gdk_wayland_subsurface_get_transform (GdkSubsurface *sub)
return wl_output_transform_to_gdk (self->transform);
}
+static gboolean
+gdk_wayland_subsurface_get_background_rect (GdkSubsurface *sub,
+ graphene_rect_t *rect)
+{
+ GdkWaylandSubsurface *self = GDK_WAYLAND_SUBSURFACE (sub);
+
+ rect->origin.x = self->bg_rect.x;
+ rect->origin.y = self->bg_rect.y;
+ rect->size.width = self->bg_rect.width;
+ rect->size.height = self->bg_rect.height;
+
+ return rect->size.width > 0 && rect->size.height > 0;
+}
+
static void
gdk_wayland_subsurface_class_init (GdkWaylandSubsurfaceClass *class)
{
@@ -440,9 +738,10 @@ gdk_wayland_subsurface_class_init (GdkWaylandSubsurfaceClass *class)
subsurface_class->attach = gdk_wayland_subsurface_attach;
subsurface_class->detach = gdk_wayland_subsurface_detach;
subsurface_class->get_texture = gdk_wayland_subsurface_get_texture;
- subsurface_class->get_source = gdk_wayland_subsurface_get_source;
- subsurface_class->get_dest = gdk_wayland_subsurface_get_dest;
+ subsurface_class->get_source_rect = gdk_wayland_subsurface_get_source_rect;
+ subsurface_class->get_texture_rect = gdk_wayland_subsurface_get_texture_rect;
subsurface_class->get_transform = gdk_wayland_subsurface_get_transform;
+ subsurface_class->get_background_rect = gdk_wayland_subsurface_get_background_rect;
};
static void
diff --git a/gdk/wayland/gdksurface-wayland.c b/gdk/wayland/gdksurface-wayland.c
index ce5f0558f8..2792cb9762 100644
--- a/gdk/wayland/gdksurface-wayland.c
+++ b/gdk/wayland/gdksurface-wayland.c
@@ -54,6 +54,8 @@
#include "linux-dmabuf-unstable-v1-client-protocol.h"
#include "presentation-time-client-protocol.h"
+#include "gsk/gskrectprivate.h"
+
/**
* GdkWaylandSurface:
@@ -657,6 +659,7 @@ static void
gdk_wayland_surface_sync_opaque_region (GdkSurface *surface)
{
GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (surface);
+ GdkWaylandDisplay *display = GDK_WAYLAND_DISPLAY (gdk_surface_get_display (surface));
struct wl_region *wl_region = NULL;
if (!impl->display_server.wl_surface)
@@ -679,16 +682,21 @@ gdk_wayland_surface_sync_opaque_region (GdkSurface *surface)
continue;
if (sub->texture != NULL)
- cairo_region_subtract_rectangle (region, &sub->dest);
+ {
+ graphene_rect_t bounds;
+ cairo_rectangle_int_t rect;
+
+ gdk_subsurface_get_bounds (subsurface, &bounds);
+ gsk_rect_to_cairo_grow (&bounds, &rect);
+ cairo_region_subtract_rectangle (region, &rect);
+ }
}
- wl_region = wl_region_from_cairo_region (GDK_WAYLAND_DISPLAY (gdk_surface_get_display (surface)),
- region);
+ wl_region = wl_region_from_cairo_region (display, region);
cairo_region_destroy (region);
}
else
- wl_region = wl_region_from_cairo_region (GDK_WAYLAND_DISPLAY (gdk_surface_get_display (surface)),
- impl->opaque_region);
+ wl_region = wl_region_from_cairo_region (display, impl->opaque_region);
}
wl_surface_set_opaque_region (impl->display_server.wl_surface, wl_region);
diff --git a/gdk/wayland/meson.build b/gdk/wayland/meson.build
index 16e5e0a6a0..0ed74bb80b 100644
--- a/gdk/wayland/meson.build
+++ b/gdk/wayland/meson.build
@@ -66,6 +66,7 @@ proto_sources = [
wlmod.find_protocol('fractional-scale', state: 'staging', version: 1),
wlmod.find_protocol('linux-dmabuf', state: 'unstable', version: 1),
wlmod.find_protocol('presentation-time', state: 'stable'),
+ wlmod.find_protocol('single-pixel-buffer', state: 'staging', version: 1),
]
gdk_wayland_gen_headers = []
diff --git a/gsk/gskoffload.c b/gsk/gskoffload.c
index 0ef9323a7c..79966d2179 100644
--- a/gsk/gskoffload.c
+++ b/gsk/gskoffload.c
@@ -81,17 +81,26 @@ find_texture_transform (GskTransform *transform)
}
static GdkTexture *
-find_texture_to_attach (GskOffload *self,
- GdkSubsurface *subsurface,
- const GskRenderNode *node,
- graphene_rect_t *out_clip,
- GdkTextureTransform *out_texture_transform)
+find_texture_to_attach (GskOffload *self,
+ const GskRenderNode *subsurface_node,
+ graphene_rect_t *out_texture_rect,
+ graphene_rect_t *out_source_rect,
+ gboolean *has_background,
+ GdkTextureTransform *out_texture_transform)
{
+ GdkSubsurface *subsurface;
+ const GskRenderNode *node;
gboolean has_clip = FALSE;
graphene_rect_t clip;
GskTransform *transform = NULL;
GdkTexture *ret = NULL;
+ *has_background = FALSE;
+ *out_texture_transform = GDK_TEXTURE_TRANSFORM_NORMAL;
+
+ subsurface = gsk_subsurface_node_get_subsurface (subsurface_node);
+ node = subsurface_node;
+
for (;;)
{
switch ((int) GSK_RENDER_NODE_TYPE (node))
@@ -100,17 +109,41 @@ find_texture_to_attach (GskOffload *self,
node = gsk_debug_node_get_child (node);
break;
- case GSK_CONTAINER_NODE:
- if (gsk_container_node_get_n_children (node) != 1)
- {
- GDK_DISPLAY_DEBUG (gdk_surface_get_display (self->surface), OFFLOAD,
- "Can't offload subsurface %p: too much content, container with %d children",
- subsurface, gsk_container_node_get_n_children (node));
- goto out;
- }
- node = gsk_container_node_get_child (node, 0);
+ case GSK_SUBSURFACE_NODE:
+ node = gsk_subsurface_node_get_child (node);
break;
+ case GSK_CONTAINER_NODE:
+ if (gsk_container_node_get_n_children (node) == 1)
+ {
+ node = gsk_container_node_get_child (node, 0);
+ break;
+ }
+ else if (gsk_container_node_get_n_children (node) == 2)
+ {
+ GskRenderNode *child = gsk_container_node_get_child (node, 0);
+ graphene_rect_t bounds;
+
+ gsk_transform_transform_bounds (transform, &child->bounds, &bounds);
+ if (GSK_RENDER_NODE_TYPE (child) == GSK_COLOR_NODE &&
+ gsk_rect_equal (&bounds, &subsurface_node->bounds) &&
+ gdk_rgba_equal (gsk_color_node_get_color (child), &(GdkRGBA) { 0, 0, 0, 1 }))
+ {
+ GDK_DISPLAY_DEBUG (gdk_surface_get_display (self->surface), OFFLOAD,
+ "Offloading subsurface %p with background",
+ subsurface);
+ *has_background = TRUE;
+
+ node = gsk_container_node_get_child (node, 1);
+ break;
+ }
+ }
+
+ GDK_DISPLAY_DEBUG (gdk_surface_get_display (self->surface), OFFLOAD,
+ "Can't offload subsurface %p: too much content, container with %d children",
+ subsurface, gsk_container_node_get_n_children (node));
+ goto out;
+
case GSK_TRANSFORM_NODE:
{
GskTransform *t = gsk_transform_node_get_transform (node);
@@ -153,6 +186,7 @@ find_texture_to_attach (GskOffload *self,
}
else
{
+ gsk_transform_transform_bounds (transform, &node->bounds, out_texture_rect);
clip = *c;
has_clip = TRUE;
}
@@ -176,17 +210,18 @@ find_texture_to_attach (GskOffload *self,
gsk_rect_intersection (&node->bounds, &clip, &clip);
- out_clip->origin.x = (clip.origin.x - dx) * sx;
- out_clip->origin.y = (clip.origin.y - dy) * sy;
- out_clip->size.width = clip.size.width * sx;
- out_clip->size.height = clip.size.height * sy;
+ out_source_rect->origin.x = (clip.origin.x - dx) * sx;
+ out_source_rect->origin.y = (clip.origin.y - dy) * sy;
+ out_source_rect->size.width = clip.size.width * sx;
+ out_source_rect->size.height = clip.size.height * sy;
}
else
{
- out_clip->origin.x = 0;
- out_clip->origin.y = 0;
- out_clip->size.width = gdk_texture_get_width (texture);
- out_clip->size.height = gdk_texture_get_height (texture);
+ gsk_transform_transform_bounds (transform, &node->bounds, out_texture_rect);
+ out_source_rect->origin.x = 0;
+ out_source_rect->origin.y = 0;
+ out_source_rect->size.width = gdk_texture_get_width (texture);
+ out_source_rect->size.height = gdk_texture_get_height (texture);
}
ret = texture;
@@ -444,7 +479,8 @@ visit_node (GskOffload *self,
if (info->can_raise)
{
- if (gsk_rect_intersects (&transformed_bounds, &info->dest))
+ if (gsk_rect_intersects (&transformed_bounds, &info->texture_rect) ||
+ gsk_rect_intersects (&transformed_bounds, &info->background_rect))
{
GskRenderNodeType type = GSK_RENDER_NODE_TYPE (node);
@@ -469,7 +505,6 @@ visit_node (GskOffload *self,
switch (GSK_RENDER_NODE_TYPE (node))
{
case GSK_BORDER_NODE:
- case GSK_COLOR_NODE:
case GSK_CONIC_GRADIENT_NODE:
case GSK_LINEAR_GRADIENT_NODE:
case GSK_REPEATING_LINEAR_GRADIENT_NODE:
@@ -479,6 +514,7 @@ visit_node (GskOffload *self,
case GSK_TEXTURE_NODE:
case GSK_TEXTURE_SCALE_NODE:
case GSK_CAIRO_NODE:
+ case GSK_COLOR_NODE:
case GSK_INSET_SHADOW_NODE:
case GSK_OUTSET_SHADOW_NODE:
case GSK_GL_SHADER_NODE:
@@ -492,7 +528,7 @@ visit_node (GskOffload *self,
case GSK_MASK_NODE:
case GSK_FILL_NODE:
case GSK_STROKE_NODE:
- break;
+ break;
case GSK_CLIP_NODE:
{
@@ -615,12 +651,16 @@ complex_clip:
}
else
{
- info->texture = find_texture_to_attach (self, subsurface, gsk_subsurface_node_get_child (node), &info->source, &info->transform);
+ gboolean has_background;
+
+ info->texture = find_texture_to_attach (self, node, &info->texture_rect, &info->source_rect, &has_background, &info->transform);
if (info->texture)
{
info->can_offload = TRUE;
info->can_raise = TRUE;
- transform_bounds (self, &node->bounds, &info->dest);
+ transform_bounds (self, &info->texture_rect, &info->texture_rect);
+ info->has_background = has_background;
+ transform_bounds (self, &node->bounds, &info->background_rect);
info->place_above = self->last_info ? self->last_info->subsurface : NULL;
self->last_info = info;
}
@@ -660,9 +700,12 @@ gsk_offload_new (GdkSurface *surface,
for (gsize i = 0; i < self->n_subsurfaces; i++)
{
GskOffloadInfo *info = &self->subsurfaces[i];
+ graphene_rect_t rect;
+
info->subsurface = gdk_surface_get_subsurface (self->surface, i);
info->was_offloaded = gdk_subsurface_get_texture (info->subsurface) != NULL;
info->was_above = gdk_subsurface_is_above_parent (info->subsurface);
+ info->had_background = gdk_subsurface_get_background_rect (info->subsurface, &rect);
}
if (self->n_subsurfaces > 0)
@@ -679,25 +722,28 @@ gsk_offload_new (GdkSurface *surface,
for (gsize i = 0; i < self->n_subsurfaces; i++)
{
GskOffloadInfo *info = &self->subsurfaces[i];
- graphene_rect_t old_dest;
+ graphene_rect_t old_bounds;
+ graphene_rect_t bounds;
- gdk_subsurface_get_dest (info->subsurface, &old_dest);
+ gdk_subsurface_get_bounds (info->subsurface, &old_bounds);
if (info->can_offload)
{
if (info->can_raise)
info->is_offloaded = gdk_subsurface_attach (info->subsurface,
info->texture,
- &info->source,
- &info->dest,
+ &info->source_rect,
+ &info->texture_rect,
info->transform,
+ info->has_background ? &info->background_rect : NULL,
TRUE, NULL);
else
info->is_offloaded = gdk_subsurface_attach (info->subsurface,
info->texture,
- &info->source,
- &info->dest,
+ &info->source_rect,
+ &info->texture_rect,
info->transform,
+ info->has_background ? &info->background_rect : NULL,
info->place_above != NULL,
info->place_above);
}
@@ -717,22 +763,24 @@ gsk_offload_new (GdkSurface *surface,
info->is_above = TRUE;
}
+ gdk_subsurface_get_bounds (info->subsurface, &bounds);
+
if (info->is_offloaded != info->was_offloaded ||
info->is_above != info->was_above ||
- (info->is_offloaded && !gsk_rect_equal (&info->dest, &old_dest)))
+ (info->is_offloaded && !gsk_rect_equal (&bounds, &old_bounds)))
{
/* We changed things, need to invalidate everything */
- cairo_rectangle_int_t int_dest;
+ cairo_rectangle_int_t rect;
if (info->is_offloaded)
{
- gsk_rect_to_cairo_grow (&info->dest, &int_dest);
- cairo_region_union_rectangle (diff, &int_dest);
+ gsk_rect_to_cairo_grow (&bounds, &rect);
+ cairo_region_union_rectangle (diff, &rect);
}
if (info->was_offloaded)
{
- gsk_rect_to_cairo_grow (&old_dest, &int_dest);
- cairo_region_union_rectangle (diff, &int_dest);
+ gsk_rect_to_cairo_grow (&old_bounds, &rect);
+ cairo_region_union_rectangle (diff, &rect);
}
}
diff --git a/gsk/gskoffloadprivate.h b/gsk/gskoffloadprivate.h
index c4c604179a..6848d83436 100644
--- a/gsk/gskoffloadprivate.h
+++ b/gsk/gskoffloadprivate.h
@@ -31,9 +31,10 @@ typedef struct
GdkSubsurface *subsurface;
GdkTexture *texture;
GdkSubsurface *place_above;
- graphene_rect_t dest;
- graphene_rect_t source;
+ graphene_rect_t texture_rect;
+ graphene_rect_t source_rect;
GdkTextureTransform transform;
+ graphene_rect_t background_rect;
guint was_offloaded : 1;
guint can_offload : 1;
@@ -42,6 +43,9 @@ typedef struct
guint was_above : 1;
guint can_raise : 1;
guint is_above : 1;
+
+ guint had_background : 1;
+ guint has_background : 1;
} GskOffloadInfo;
GskOffload * gsk_offload_new (GdkSurface *surface,
diff --git a/gtk/inspector/general.c b/gtk/inspector/general.c
index 027eb0ad72..e025557e59 100644
--- a/gtk/inspector/general.c
+++ b/gtk/inspector/general.c
@@ -694,6 +694,7 @@ add_wayland_protocols (GdkDisplay *display,
append_wayland_protocol_row (gen, (struct wl_proxy *)d->fractional_scale);
append_wayland_protocol_row (gen, (struct wl_proxy *)d->viewporter);
append_wayland_protocol_row (gen, (struct wl_proxy *)d->presentation);
+ append_wayland_protocol_row (gen, (struct wl_proxy *)d->single_pixel_buffer);
}
}
#endif
diff --git a/gtk/inspector/subsurfaceoverlay.c b/gtk/inspector/subsurfaceoverlay.c
index 8e5a203365..7701ebcff3 100644
--- a/gtk/inspector/subsurfaceoverlay.c
+++ b/gtk/inspector/subsurfaceoverlay.c
@@ -50,7 +50,7 @@ gtk_subsurface_overlay_snapshot (GtkInspectorOverlay *overlay,
else
gdk_rgba_parse (&color, "magenta");
- gdk_subsurface_get_dest (subsurface, &dest);
+ gdk_subsurface_get_texture_rect (subsurface, &dest);
/* Use 4 color nodes since a border node overlaps and prevents
* the subsurface from being raised.
diff --git a/gtk/theme/Default/_common.scss b/gtk/theme/Default/_common.scss
index 5197eeb84a..883e37fe8c 100644
--- a/gtk/theme/Default/_common.scss
+++ b/gtk/theme/Default/_common.scss
@@ -3940,7 +3940,9 @@ video {
min-height: 64px;
border-radius: 32px;
}
- background: black;
+ & graphicsoffload > picture {
+ background: black;
+ }
}
/************
diff --git a/testsuite/gdk/subsurface.c b/testsuite/gdk/subsurface.c
index 37657e8049..3a63fe44b9 100644
--- a/testsuite/gdk/subsurface.c
+++ b/testsuite/gdk/subsurface.c
@@ -1,6 +1,7 @@
#include
#include "gdk/gdksurfaceprivate.h"
#include "gdk/gdksubsurfaceprivate.h"
+#include "gdk/gdkdebugprivate.h"
#ifdef GDK_WINDOWING_WAYLAND
#include "gdk/wayland/gdkwayland.h"
@@ -11,6 +12,53 @@
gdk_texture_get_width (t), \
gdk_texture_get_height (t))
+static void
+test_subsurface_basics (void)
+{
+ GdkSurface *surface;
+ GdkSubsurface *sub;
+ GdkTexture *texture;
+ graphene_rect_t rect;
+
+#ifdef GDK_WINDOWING_WAYLAND
+ if (!GDK_IS_WAYLAND_DISPLAY (gdk_display_get_default ()))
+#endif
+ {
+ g_test_skip ("No subsurface support");
+ return;
+ }
+
+ surface = gdk_surface_new_toplevel (gdk_display_get_default ());
+
+ g_assert_true (surface->subsurfaces_below == NULL);
+ g_assert_true (surface->subsurfaces_above == NULL);
+
+ sub = gdk_surface_create_subsurface (surface);
+
+ g_assert_true (gdk_subsurface_get_parent (sub) == surface);
+
+ g_assert_null (gdk_subsurface_get_texture (sub));
+ g_assert_false (gdk_subsurface_is_above_parent (sub));
+ g_assert_true (gdk_subsurface_get_transform (sub) == GDK_TEXTURE_TRANSFORM_NORMAL);
+
+ texture = gdk_texture_new_from_resource ("/org/gtk/libgtk/icons/16x16/actions/media-eject.png");
+ gdk_subsurface_attach (sub, texture, &TEXTURE_RECT (texture), &GRAPHENE_RECT_INIT (0, 0, 10, 10), GDK_TEXTURE_TRANSFORM_90, &GRAPHENE_RECT_INIT (0, 0, 20, 20), TRUE, NULL);
+
+ g_assert_true (gdk_subsurface_get_texture (sub) == texture);
+ g_assert_true (gdk_subsurface_is_above_parent (sub));
+ g_assert_true (gdk_subsurface_get_transform (sub) == GDK_TEXTURE_TRANSFORM_90);
+ gdk_subsurface_get_source_rect (sub, &rect);
+ g_assert_true (graphene_rect_equal (&rect, &TEXTURE_RECT (texture)));
+ gdk_subsurface_get_texture_rect (sub, &rect);
+ g_assert_true (graphene_rect_equal (&rect, &GRAPHENE_RECT_INIT (0, 0, 10, 10)));
+ gdk_subsurface_get_background_rect (sub, &rect);
+ g_assert_true (graphene_rect_equal (&rect, &GRAPHENE_RECT_INIT (0, 0, 20, 20)));
+
+ g_object_unref (sub);
+ g_object_unref (texture);
+ gdk_surface_destroy (surface);
+}
+
static void
test_subsurface_stacking (void)
{
@@ -42,9 +90,9 @@ test_subsurface_stacking (void)
texture = gdk_texture_new_from_resource ("/org/gtk/libgtk/icons/16x16/actions/media-eject.png");
- gdk_subsurface_attach (sub0, texture, &TEXTURE_RECT (texture), &GRAPHENE_RECT_INIT (0, 0, 10, 10), GDK_TEXTURE_TRANSFORM_NORMAL, TRUE, NULL);
- gdk_subsurface_attach (sub1, texture, &TEXTURE_RECT (texture), &GRAPHENE_RECT_INIT (0, 0, 10, 10), GDK_TEXTURE_TRANSFORM_NORMAL, TRUE, NULL);
- gdk_subsurface_attach (sub2, texture, &TEXTURE_RECT (texture), &GRAPHENE_RECT_INIT (0, 0, 10, 10), GDK_TEXTURE_TRANSFORM_NORMAL, TRUE, NULL);
+ gdk_subsurface_attach (sub0, texture, &TEXTURE_RECT (texture), &GRAPHENE_RECT_INIT (0, 0, 10, 10), GDK_TEXTURE_TRANSFORM_NORMAL, NULL, TRUE, NULL);
+ gdk_subsurface_attach (sub1, texture, &TEXTURE_RECT (texture), &GRAPHENE_RECT_INIT (0, 0, 10, 10), GDK_TEXTURE_TRANSFORM_NORMAL, NULL, TRUE, NULL);
+ gdk_subsurface_attach (sub2, texture, &TEXTURE_RECT (texture), &GRAPHENE_RECT_INIT (0, 0, 10, 10), GDK_TEXTURE_TRANSFORM_NORMAL, NULL, TRUE, NULL);
g_assert_true (surface->subsurfaces_above == sub2);
g_assert_true (sub2->sibling_below == NULL);
@@ -67,7 +115,7 @@ test_subsurface_stacking (void)
g_assert_true (sub0->sibling_above == NULL);
g_assert_true (sub0->above_parent);
- gdk_subsurface_attach (sub2, texture, &TEXTURE_RECT (texture), &GRAPHENE_RECT_INIT (0, 0, 10, 10), GDK_TEXTURE_TRANSFORM_NORMAL, FALSE, NULL);
+ gdk_subsurface_attach (sub2, texture, &TEXTURE_RECT (texture), &GRAPHENE_RECT_INIT (0, 0, 10, 10), GDK_TEXTURE_TRANSFORM_NORMAL, NULL, FALSE, NULL);
g_assert_true (surface->subsurfaces_above == sub0);
g_assert_true (sub0->sibling_below == NULL);
@@ -79,7 +127,7 @@ test_subsurface_stacking (void)
g_assert_true (sub2->sibling_above == NULL);
g_assert_false (sub2->above_parent);
- gdk_subsurface_attach (sub1, texture, &TEXTURE_RECT (texture), &GRAPHENE_RECT_INIT (0, 0, 10, 10), GDK_TEXTURE_TRANSFORM_NORMAL, TRUE, sub2);
+ gdk_subsurface_attach (sub1, texture, &TEXTURE_RECT (texture), &GRAPHENE_RECT_INIT (0, 0, 10, 10), GDK_TEXTURE_TRANSFORM_NORMAL, NULL, TRUE, sub2);
g_assert_true (surface->subsurfaces_below == sub1);
g_assert_true (sub1->sibling_above == NULL);
@@ -103,6 +151,9 @@ main (int argc, char *argv[])
{
gtk_test_init (&argc, &argv, NULL);
+ gdk_display_set_debug_flags (gdk_display_get_default (), GDK_DEBUG_FORCE_OFFLOAD);
+
+ g_test_add_func ("/subsurface/basics", test_subsurface_basics);
g_test_add_func ("/subsurface/stacking", test_subsurface_stacking);
return g_test_run ();
diff --git a/testsuite/gsk/meson.build b/testsuite/gsk/meson.build
index d8776c451a..a7289b6e64 100644
--- a/testsuite/gsk/meson.build
+++ b/testsuite/gsk/meson.build
@@ -478,6 +478,8 @@ if os_linux
'clipped.node',
'not-clipped.node',
'complex-clip.node',
+ 'background.node',
+ 'background2.node',
]
foreach test : offload_tests
@@ -487,10 +489,10 @@ if os_linux
join_paths(meson.current_source_dir(), 'offload', test)
],
env: [
- 'GSK_RENDERER=opengl',
'GTK_A11Y=test',
'G_TEST_SRCDIR=@0@'.format(meson.current_source_dir()),
- 'G_TEST_BUILDDIR=@0@'.format(meson.current_build_dir())
+ 'G_TEST_BUILDDIR=@0@'.format(meson.current_build_dir()),
+ 'GDK_DEBUG=force-offload',
],
protocol: 'exitcode',
suite: ['gsk', 'offload'],
diff --git a/testsuite/gsk/offload.c b/testsuite/gsk/offload.c
index 3e2b186411..03c9611d70 100644
--- a/testsuite/gsk/offload.c
+++ b/testsuite/gsk/offload.c
@@ -210,11 +210,16 @@ collect_offload_info (GdkSurface *surface,
gdk_texture_get_width (info->texture),
gdk_texture_get_height (info->texture));
g_string_append_printf (s, "source: %g %g %g %g, ",
- info->source.origin.x, info->source.origin.y,
- info->source.size.width, info->source.size.height);
- g_string_append_printf (s, "dest: %g %g %g %g\n",
- info->dest.origin.x, info->dest.origin.y,
- info->dest.size.width, info->dest.size.height);
+ info->source_rect.origin.x, info->source_rect.origin.y,
+ info->source_rect.size.width, info->source_rect.size.height);
+ g_string_append_printf (s, "dest: %g %g %g %g",
+ info->texture_rect.origin.x, info->texture_rect.origin.y,
+ info->texture_rect.size.width, info->texture_rect.size.height);
+ if (info->has_background)
+ g_string_append_printf (s, ", background: %g %g %g %g",
+ info->background_rect.origin.x, info->background_rect.origin.y,
+ info->background_rect.size.width, info->background_rect.size.height);
+ g_string_append (s, "\n");
}
else
g_string_append_printf (s, "%u: %snot offloaded\n",
@@ -416,8 +421,11 @@ parse_node_file (GFile *file, const char *generate)
g_assert_no_error (error);
if (diff && g_bytes_get_size (diff) > 0)
{
- g_print ("Resulting .offload file doesn't match reference:\n%s\n",
+ char *basename = g_path_get_basename (reference_file);
+ g_print ("Resulting file doesn't match reference (%s):\n%s\n",
+ basename,
(const char *) g_bytes_get_data (diff, NULL));
+ g_free (basename);
result = FALSE;
}
@@ -452,8 +460,11 @@ parse_node_file (GFile *file, const char *generate)
g_assert_no_error (error);
if (diff && g_bytes_get_size (diff) > 0)
{
- g_print ("Resulting .offload2 file doesn't match reference:\n%s\n",
+ char *basename = g_path_get_basename (reference_file);
+ g_print ("Resulting file doesn't match reference (%s):\n%s\n",
+ basename,
(const char *) g_bytes_get_data (diff, NULL));
+ g_free (basename);
result = FALSE;
}
diff --git a/testsuite/gsk/offload/background.node b/testsuite/gsk/offload/background.node
new file mode 100644
index 0000000000..b3078ab81d
--- /dev/null
+++ b/testsuite/gsk/offload/background.node
@@ -0,0 +1,71 @@
+container {
+
+ transform {
+ child: subsurface {
+ child: container {
+ color {
+ bounds: 0 0 100 100;
+ color: black;
+ }
+ texture {
+ texture: url('data:image/svg+xml;utf-8,');
+ }
+ }
+ }
+ }
+
+ debug {
+ message: "Non-black background doesn't work";
+ child: subsurface {
+ child: container {
+ color {
+ bounds: 0 0 100 100;
+ color: red;
+ }
+ texture {
+ texture: url('data:image/svg+xml;utf-8,');
+ }
+ }
+ }
+ }
+
+ debug {
+ message: "Can't have too much content";
+ child: subsurface {
+ child: container {
+ color {
+ bounds: 0 0 100 100;
+ color: black;
+ }
+ color {
+ bounds: 0 0 20 20;
+ color: red;
+ }
+ texture {
+ texture: url('data:image/svg+xml;utf-8,');
+ }
+ }
+ }
+ }
+
+ debug {
+ message: "Texture can be deeper in";
+ child: subsurface {
+ child: container {
+ color {
+ bounds: 0 0 100 100;
+ color: black;
+ }
+ container {
+ debug {
+ message: "bla";
+ child: texture {
+ texture: url('data:image/svg+xml;utf-8,');
+ }
+ }
+ }
+ }
+ }
+ }
+
+}
diff --git a/testsuite/gsk/offload/background.offload b/testsuite/gsk/offload/background.offload
new file mode 100644
index 0000000000..e6bedfa12a
--- /dev/null
+++ b/testsuite/gsk/offload/background.offload
@@ -0,0 +1,4 @@
+0: offloaded, above: -, texture: 13x17, source: 0 0 13 17, dest: 0 0 50 50, background: 0 0 100 100
+1: not offloaded
+2: not offloaded
+3: offloaded, raised, above: 0, texture: 13x17, source: 0 0 13 17, dest: 0 0 50 50, background: 0 0 100 100
diff --git a/testsuite/gsk/offload/background2.diff b/testsuite/gsk/offload/background2.diff
new file mode 100644
index 0000000000..542ff51bdc
--- /dev/null
+++ b/testsuite/gsk/offload/background2.diff
@@ -0,0 +1 @@
+0 0 50 50
diff --git a/testsuite/gsk/offload/background2.node b/testsuite/gsk/offload/background2.node
new file mode 100644
index 0000000000..a6d9d0d547
--- /dev/null
+++ b/testsuite/gsk/offload/background2.node
@@ -0,0 +1,15 @@
+container {
+
+ subsurface {
+ child: container {
+ color {
+ bounds: 0 0 100 100;
+ color: black;
+ }
+ texture {
+ texture: url('data:image/svg+xml;utf-8,');
+ }
+ }
+ }
+
+}
diff --git a/testsuite/gsk/offload/background2.node2 b/testsuite/gsk/offload/background2.node2
new file mode 100644
index 0000000000..bb670adcfc
--- /dev/null
+++ b/testsuite/gsk/offload/background2.node2
@@ -0,0 +1,22 @@
+container {
+
+ subsurface {
+ child: container {
+ color {
+ bounds: 0 0 100 100;
+ color: black;
+ }
+ texture {
+ texture: url('data:image/svg+xml;utf-8,');
+ }
+ }
+ }
+
+ debug {
+ message: "Put something on top, so we switch from above to below";
+ child: color {
+ bounds: 0 0 10 10;
+ color: red;
+ }
+ }
+}
diff --git a/testsuite/gsk/offload/background2.offload b/testsuite/gsk/offload/background2.offload
new file mode 100644
index 0000000000..d100720f95
--- /dev/null
+++ b/testsuite/gsk/offload/background2.offload
@@ -0,0 +1 @@
+0: offloaded, raised, above: -, texture: 13x17, source: 0 0 13 17, dest: 0 0 50 50, background: 0 0 100 100
diff --git a/testsuite/gsk/offload/background2.offload2 b/testsuite/gsk/offload/background2.offload2
new file mode 100644
index 0000000000..e128259b60
--- /dev/null
+++ b/testsuite/gsk/offload/background2.offload2
@@ -0,0 +1 @@
+0: offloaded, was offloaded, above: -, texture: 13x17, source: 0 0 13 17, dest: 0 0 50 50, background: 0 0 100 100
diff --git a/testsuite/gsk/offload/deep.node b/testsuite/gsk/offload/deep.node
index 459094bf3d..d553c8445d 100644
--- a/testsuite/gsk/offload/deep.node
+++ b/testsuite/gsk/offload/deep.node
@@ -43,7 +43,7 @@ subsurface {
}
subsurface {
child: debug {
- message: "Clips (regardless how large) are not ok";
+ message: "Clips are ok";
child: clip {
clip: 0 0 400 400;
child: texture { }
diff --git a/testsuite/gsk/offload/move.offload2 b/testsuite/gsk/offload/move.offload2
index b656abf6a1..65d6b78c5f 100644
--- a/testsuite/gsk/offload/move.offload2
+++ b/testsuite/gsk/offload/move.offload2
@@ -1 +1 @@
-0: offloaded, raised, above: -, texture: 16x16, source: 0 0 16 16, dest: 40 40 20 20
+0: offloaded, was offloaded, raised, above: -, texture: 16x16, source: 0 0 16 16, dest: 40 40 20 20
diff --git a/testsuite/gsk/offload/start_offloading.diff b/testsuite/gsk/offload/start_offloading.diff
index 039342357c..08f07ec9af 100644
--- a/testsuite/gsk/offload/start_offloading.diff
+++ b/testsuite/gsk/offload/start_offloading.diff
@@ -1,2 +1 @@
-0 0 16 16
16 16 16 16
diff --git a/testsuite/gsk/offload/stop_offloading.offload2 b/testsuite/gsk/offload/stop_offloading.offload2
index 94737aed76..cd6081cb46 100644
--- a/testsuite/gsk/offload/stop_offloading.offload2
+++ b/testsuite/gsk/offload/stop_offloading.offload2
@@ -1 +1 @@
-0: not offloaded
+0: was offloaded, not offloaded