From 1c8b22cf059333610f36e2d192b755c93bfa366c Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sun, 14 Apr 2024 08:58:20 -0400 Subject: [PATCH 01/17] subsurface: Cosmetics --- gdk/gdksubsurface.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gdk/gdksubsurface.c b/gdk/gdksubsurface.c index 5f0a26f62e..ba275a6ec9 100644 --- a/gdk/gdksubsurface.c +++ b/gdk/gdksubsurface.c @@ -200,7 +200,7 @@ gdk_subsurface_get_dest (GdkSubsurface *subsurface, 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; } From 26cc96bb7f24a2ea9da1301778854480f0f7b4f5 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sun, 14 Apr 2024 20:36:59 -0400 Subject: [PATCH 02/17] testsuite: Cosmetics Correct a debug message. --- testsuite/gsk/offload/deep.node | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 { } From 0fb1f44eec7df10fc3346adf767b6e1f3ae19e55 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sat, 13 Apr 2024 14:09:31 -0400 Subject: [PATCH 03/17] gdk: Add a debug flag to allow non-dmabuf offload This is useful for debugging offloading without having to rely on gstreamer giving us dmabufs. To use it, set GDK_DEBUG=force-offload in the environment. --- docs/reference/gtk/running.md | 4 ++ gdk/gdk.c | 1 + gdk/gdkdebugprivate.h | 2 +- gdk/wayland/gdksubsurface-wayland.c | 68 +++++++++++++++++++++++++++-- 4 files changed, 71 insertions(+), 4 deletions(-) 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/wayland/gdksubsurface-wayland.c b/gdk/wayland/gdksubsurface-wayland.c index cecd10cb28..45d03daeea 100644 --- a/gdk/wayland/gdksubsurface-wayland.c +++ b/gdk/wayland/gdksubsurface-wayland.c @@ -50,6 +50,48 @@ gdk_wayland_subsurface_finalize (GObject *object) 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 +138,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 +190,25 @@ 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 inline enum wl_output_transform gdk_texture_transform_to_wl (GdkTextureTransform transform) { @@ -239,7 +300,8 @@ gdk_wayland_subsurface_attach (GdkSubsurface *sub, scale, self); } - else if (!GDK_IS_DMABUF_TEXTURE (texture)) + 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", From a70998aa5097717b44395c833d9d65f67bc46491 Mon Sep 17 00:00:00 2001 From: Robert Mader Date: Mon, 18 Mar 2024 21:15:57 +0100 Subject: [PATCH 04/17] wayland: Support the single-pixel-buffer protocol This just gets the manager object. It is not used yet. --- gdk/wayland/gdkdisplay-wayland.c | 9 ++++++++- gdk/wayland/gdkdisplay-wayland.h | 2 ++ gdk/wayland/meson.build | 1 + gtk/inspector/general.c | 1 + 4 files changed, 12 insertions(+), 1 deletion(-) 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/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/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 From 3f9bdaa4c858fde15d69674c70dd48765277da86 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sat, 13 Apr 2024 14:20:01 -0400 Subject: [PATCH 05/17] Add background to subsurfaces Make it possible for subsurfaces to have a black background on a secondary subsurface below the actual subsurface. Using a single-pixel buffer for that background increases the changes that the compositor will use direct scanout for the actual subsurface. This changes the private subsurface API. All callers have been updated to pass an empty background rect. --- gdk/gdksubsurface.c | 19 +- gdk/gdksubsurface.c.orig | 222 ++++++++++++++++++++ gdk/gdksubsurface.c.rej | 16 ++ gdk/gdksubsurfaceprivate.h | 8 +- gdk/gdksubsurfaceprivate.h.orig | 107 ++++++++++ gdk/wayland/gdksubsurface-wayland-private.h | 6 + gdk/wayland/gdksubsurface-wayland.c | 182 ++++++++++++++-- gsk/gskoffload.c | 2 + testsuite/gdk/subsurface.c | 10 +- 9 files changed, 551 insertions(+), 21 deletions(-) create mode 100644 gdk/gdksubsurface.c.orig create mode 100644 gdk/gdksubsurface.c.rej create mode 100644 gdk/gdksubsurfaceprivate.h.orig diff --git a/gdk/gdksubsurface.c b/gdk/gdksubsurface.c index ba275a6ec9..9f124639d2 100644 --- a/gdk/gdksubsurface.c +++ b/gdk/gdksubsurface.c @@ -114,6 +114,7 @@ gdk_subsurface_attach (GdkSubsurface *subsurface, const graphene_rect_t *source, const graphene_rect_t *dest, GdkTextureTransform transform, + const graphene_rect_t *background, gboolean above, GdkSubsurface *sibling) { @@ -156,7 +157,14 @@ gdk_subsurface_attach (GdkSubsurface *subsurface, } } - return GDK_SUBSURFACE_GET_CLASS (subsurface)->attach (subsurface, texture, source, dest, transform, above, sibling); + return GDK_SUBSURFACE_GET_CLASS (subsurface)->attach (subsurface, + texture, + source, + dest, + transform, + background, + above, + sibling); } void @@ -213,3 +221,12 @@ 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/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..f7efc5add5 100644 --- a/gdk/gdksubsurfaceprivate.h +++ b/gdk/gdksubsurfaceprivate.h @@ -67,6 +67,7 @@ struct _GdkSubsurfaceClass 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); @@ -77,6 +78,8 @@ struct _GdkSubsurfaceClass graphene_rect_t *dest); GdkTextureTransform (* get_transform) (GdkSubsurface *subsurface); + void (* get_background) (GdkSubsurface *subsurface, + graphene_rect_t *background); }; GType gdk_subsurface_get_type (void) G_GNUC_CONST; @@ -88,6 +91,7 @@ gboolean gdk_subsurface_attach (GdkSubsurface *subsurfac 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); @@ -95,10 +99,12 @@ GdkTexture * gdk_subsurface_get_texture (GdkSubsurface *subsurfac void gdk_subsurface_get_source (GdkSubsurface *subsurface, graphene_rect_t *source); void gdk_subsurface_get_dest (GdkSubsurface *subsurface, - graphene_rect_t *dest); + graphene_rect_t *dest); gboolean gdk_subsurface_is_above_parent (GdkSubsurface *subsurface); GdkTextureTransform gdk_subsurface_get_transform (GdkSubsurface *subsurface); +void gdk_subsurface_get_background (GdkSubsurface *subsurface, + graphene_rect_t *background); 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/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 45d03daeea..c27643f781 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,6 +48,9 @@ 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); } @@ -209,6 +214,33 @@ get_wl_buffer (GdkWaylandSubsurface *self, 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) { @@ -221,12 +253,59 @@ 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) { @@ -238,7 +317,7 @@ gdk_wayland_subsurface_attach (GdkSubsurface *sub, gboolean will_be_above; double scale; graphene_rect_t device_rect; - cairo_rectangle_int_t device_dest; + gboolean has_background; if (sibling) will_be_above = sibling->above_parent; @@ -265,15 +344,22 @@ gdk_wayland_subsurface_attach (GdkSubsurface *sub, 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) + { + 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 + { + 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 (self->dest.x != dest->origin.x || self->dest.y != dest->origin.y || @@ -288,15 +374,21 @@ 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 (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); } @@ -397,6 +489,38 @@ gdk_wayland_subsurface_attach (GdkSubsurface *sub, } + if (has_background) + { + ensure_bg_surface (self); + + if (self->bg_surface) + { + 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); + 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)); + + if (!self->bg_attached) + { + self->bg_attached = TRUE; + + wl_surface_attach (self->bg_surface, get_sp_buffer (self), 0, 0); + wl_surface_damage_buffer (self->bg_surface, 0, 0, 1, 1); + } + } + } + else + { + if (self->bg_surface && self->bg_attached) + { + self->bg_attached = FALSE; + wl_surface_attach (self->bg_surface, NULL, 0, 0); + } + } + result = TRUE; } else @@ -404,6 +528,11 @@ gdk_wayland_subsurface_attach (GdkSubsurface *sub, g_set_object (&self->texture, NULL); wl_surface_attach (self->surface, NULL, 0, 0); + if (self->bg_surface) + { + self->bg_attached = FALSE; + wl_surface_attach (self->bg_surface, NULL, 0, 0); + } } if (sib) @@ -423,7 +552,12 @@ gdk_wayland_subsurface_attach (GdkSubsurface *sub, GDK_WAYLAND_SURFACE (sub->parent)->display_server.wl_surface); } + if (self->bg_attached) + wl_subsurface_place_below (self->bg_subsurface, self->surface); + wl_surface_commit (self->surface); + if (self->bg_surface) + wl_surface_commit (self->bg_surface); ((GdkWaylandSurface *)sub->parent)->has_pending_subsurface_commits = TRUE; GDK_WAYLAND_SURFACE (sub->parent)->opaque_region_dirty = TRUE; @@ -447,6 +581,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; } @@ -491,6 +632,18 @@ gdk_wayland_subsurface_get_transform (GdkSubsurface *sub) return wl_output_transform_to_gdk (self->transform); } +static void +gdk_wayland_subsurface_get_background (GdkSubsurface *sub, + graphene_rect_t *background) +{ + GdkWaylandSubsurface *self = GDK_WAYLAND_SUBSURFACE (sub); + + background->origin.x = self->bg_rect.x; + background->origin.y = self->bg_rect.y; + background->size.width = self->bg_rect.width; + background->size.height = self->bg_rect.height; +} + static void gdk_wayland_subsurface_class_init (GdkWaylandSubsurfaceClass *class) { @@ -505,6 +658,7 @@ gdk_wayland_subsurface_class_init (GdkWaylandSubsurfaceClass *class) subsurface_class->get_source = gdk_wayland_subsurface_get_source; subsurface_class->get_dest = gdk_wayland_subsurface_get_dest; subsurface_class->get_transform = gdk_wayland_subsurface_get_transform; + subsurface_class->get_background = gdk_wayland_subsurface_get_background; }; static void diff --git a/gsk/gskoffload.c b/gsk/gskoffload.c index 0ef9323a7c..5892562e64 100644 --- a/gsk/gskoffload.c +++ b/gsk/gskoffload.c @@ -691,6 +691,7 @@ gsk_offload_new (GdkSurface *surface, &info->source, &info->dest, info->transform, + &GRAPHENE_RECT_INIT (0, 0, 0, 0), TRUE, NULL); else info->is_offloaded = gdk_subsurface_attach (info->subsurface, @@ -698,6 +699,7 @@ gsk_offload_new (GdkSurface *surface, &info->source, &info->dest, info->transform, + &GRAPHENE_RECT_INIT (0, 0, 0, 0), info->place_above != NULL, info->place_above); } diff --git a/testsuite/gdk/subsurface.c b/testsuite/gdk/subsurface.c index 37657e8049..4b80fb2023 100644 --- a/testsuite/gdk/subsurface.c +++ b/testsuite/gdk/subsurface.c @@ -42,9 +42,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 +67,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 +79,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); From c9954734075d61a13ae1ff29bbc6214c1223d88c Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sun, 14 Apr 2024 08:58:52 -0400 Subject: [PATCH 06/17] Add more subsurface tests Add some more tests for the basic functioning of the subsurface API. --- testsuite/gdk/subsurface.c | 51 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/testsuite/gdk/subsurface.c b/testsuite/gdk/subsurface.c index 4b80fb2023..383b936029 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 (sub, &rect); + g_assert_true (graphene_rect_equal (&rect, &TEXTURE_RECT (texture))); + gdk_subsurface_get_dest (sub, &rect); + g_assert_true (graphene_rect_equal (&rect, &GRAPHENE_RECT_INIT (0, 0, 10, 10))); + gdk_subsurface_get_background (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) { @@ -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 (); From 0108a5f56db6229777c660684284a7790a990d5a Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sat, 13 Apr 2024 14:25:42 -0400 Subject: [PATCH 07/17] offload: Use subsurface background optimization Detect a black color node below the texture node and pass that information to the subsurface, to take advange of the single-pixel buffer optimization. To make this work, we need to stop using the bounds of the subsurface node for sizing the offload, and instead use either the clip or the texture node for that. --- gsk/gskoffload.c | 105 +++++++++++++++++++++++++++++----------- gsk/gskoffloadprivate.h | 1 + 2 files changed, 79 insertions(+), 27 deletions(-) diff --git a/gsk/gskoffload.c b/gsk/gskoffload.c index 5892562e64..55fd14e7b4 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_bounds, + graphene_rect_t *out_clip, + 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_bounds); clip = *c; has_clip = TRUE; } @@ -183,6 +217,7 @@ find_texture_to_attach (GskOffload *self, } else { + gsk_transform_transform_bounds (transform, &node->bounds, out_bounds); out_clip->origin.x = 0; out_clip->origin.y = 0; out_clip->size.width = gdk_texture_get_width (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->dest) || + gsk_rect_intersects (&transformed_bounds, &info->bg)) { 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,19 @@ 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->dest, &info->source, &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->dest, &info->dest); + if (has_background) + transform_bounds (self, &node->bounds, &info->bg); + else + graphene_rect_init (&info->bg, 0, 0, 0, 0); + info->place_above = self->last_info ? self->last_info->subsurface : NULL; self->last_info = info; } @@ -680,8 +723,10 @@ gsk_offload_new (GdkSurface *surface, { GskOffloadInfo *info = &self->subsurfaces[i]; graphene_rect_t old_dest; + graphene_rect_t old_background; gdk_subsurface_get_dest (info->subsurface, &old_dest); + gdk_subsurface_get_background (info->subsurface, &old_background); if (info->can_offload) { @@ -691,7 +736,7 @@ gsk_offload_new (GdkSurface *surface, &info->source, &info->dest, info->transform, - &GRAPHENE_RECT_INIT (0, 0, 0, 0), + &info->bg, TRUE, NULL); else info->is_offloaded = gdk_subsurface_attach (info->subsurface, @@ -699,7 +744,7 @@ gsk_offload_new (GdkSurface *surface, &info->source, &info->dest, info->transform, - &GRAPHENE_RECT_INIT (0, 0, 0, 0), + &info->bg, info->place_above != NULL, info->place_above); } @@ -721,20 +766,26 @@ gsk_offload_new (GdkSurface *surface, 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 (&info->dest, &old_dest) || + !gsk_rect_equal (&info->bg, &old_background)))) { /* 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 (&info->dest, &rect); + cairo_region_union_rectangle (diff, &rect); + gsk_rect_to_cairo_grow (&info->bg, &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_dest, &rect); + cairo_region_union_rectangle (diff, &rect); + gsk_rect_to_cairo_grow (&old_background, &rect); + cairo_region_union_rectangle (diff, &rect); } } diff --git a/gsk/gskoffloadprivate.h b/gsk/gskoffloadprivate.h index c4c604179a..ec70ae692a 100644 --- a/gsk/gskoffloadprivate.h +++ b/gsk/gskoffloadprivate.h @@ -34,6 +34,7 @@ typedef struct graphene_rect_t dest; graphene_rect_t source; GdkTextureTransform transform; + graphene_rect_t bg; guint was_offloaded : 1; guint can_offload : 1; From e33e9c506d51ee5342c3ce0ea68197f620da166c Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sat, 13 Apr 2024 21:51:34 -0400 Subject: [PATCH 08/17] theme: Move the black background of the video We need the black inside the subsurface node for the single-pixel optimization to work, so put it there. --- gtk/theme/Default/_common.scss | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) 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; + } } /************ From 933a0e5a98166fb8e1e47c1f292959fdd65ffeda Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sun, 14 Apr 2024 18:25:41 -0400 Subject: [PATCH 09/17] subsurface: Some api revision and documentation Rename things so they make more sense. The dest/source naming got a bit unclear when we added background into the mix. Now we're going for: source_rect - the texture region to display texture_rect - dimensions of the subsurface showing the texture background_rect - dimensions of the background subsurface bounds - union of texture_rect and background_rect Also use this opportunity to add some api docs. --- gdk/gdksubsurface.c | 143 +++++++++++++++++++++++++--- gdk/gdksubsurfaceprivate.h | 76 ++++++++------- gdk/wayland/gdksubsurface-wayland.c | 46 ++++----- gsk/gskoffload.c | 4 +- gtk/inspector/subsurfaceoverlay.c | 2 +- testsuite/gdk/subsurface.c | 6 +- 6 files changed, 198 insertions(+), 79 deletions(-) diff --git a/gdk/gdksubsurface.c b/gdk/gdksubsurface.c index 9f124639d2..128a7f26d7 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,6 +116,29 @@ 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, @@ -167,6 +198,14 @@ gdk_subsurface_attach (GdkSubsurface *subsurface, sibling); } +/*< 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) { @@ -177,6 +216,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) { @@ -185,26 +232,52 @@ 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) { @@ -213,6 +286,15 @@ gdk_subsurface_is_above_parent (GdkSubsurface *subsurface) return subsurface->above_parent; } +/*< 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) { @@ -221,12 +303,45 @@ 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) +/*< 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_if_fail (GDK_IS_SUBSURFACE (subsurface)); - g_return_if_fail (background != NULL); + g_return_val_if_fail (GDK_IS_SUBSURFACE (subsurface), FALSE); + g_return_val_if_fail (rect != NULL, FALSE); - GDK_SUBSURFACE_GET_CLASS (subsurface)->get_background (subsurface, background); + 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/gdksubsurfaceprivate.h b/gdk/gdksubsurfaceprivate.h index f7efc5add5..c588676e6c 100644 --- a/gdk/gdksubsurfaceprivate.h +++ b/gdk/gdksubsurfaceprivate.h @@ -62,49 +62,51 @@ 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); + 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); - void (* get_background) (GdkSubsurface *subsurface, - graphene_rect_t *background); + (* 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, - 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); +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); GdkTextureTransform - gdk_subsurface_get_transform (GdkSubsurface *subsurface); -void gdk_subsurface_get_background (GdkSubsurface *subsurface, - graphene_rect_t *background); + 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/wayland/gdksubsurface-wayland.c b/gdk/wayland/gdksubsurface-wayland.c index c27643f781..8d05e06995 100644 --- a/gdk/wayland/gdksubsurface-wayland.c +++ b/gdk/wayland/gdksubsurface-wayland.c @@ -601,27 +601,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 @@ -632,16 +632,18 @@ gdk_wayland_subsurface_get_transform (GdkSubsurface *sub) return wl_output_transform_to_gdk (self->transform); } -static void -gdk_wayland_subsurface_get_background (GdkSubsurface *sub, - graphene_rect_t *background) +static gboolean +gdk_wayland_subsurface_get_background_rect (GdkSubsurface *sub, + graphene_rect_t *rect) { GdkWaylandSubsurface *self = GDK_WAYLAND_SUBSURFACE (sub); - background->origin.x = self->bg_rect.x; - background->origin.y = self->bg_rect.y; - background->size.width = self->bg_rect.width; - background->size.height = self->bg_rect.height; + 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 @@ -655,10 +657,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 = gdk_wayland_subsurface_get_background; + subsurface_class->get_background_rect = gdk_wayland_subsurface_get_background_rect; }; static void diff --git a/gsk/gskoffload.c b/gsk/gskoffload.c index 55fd14e7b4..226ab7d85c 100644 --- a/gsk/gskoffload.c +++ b/gsk/gskoffload.c @@ -725,8 +725,8 @@ gsk_offload_new (GdkSurface *surface, graphene_rect_t old_dest; graphene_rect_t old_background; - gdk_subsurface_get_dest (info->subsurface, &old_dest); - gdk_subsurface_get_background (info->subsurface, &old_background); + gdk_subsurface_get_texture_rect (info->subsurface, &old_dest); + gdk_subsurface_get_background_rect (info->subsurface, &old_background); if (info->can_offload) { 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/testsuite/gdk/subsurface.c b/testsuite/gdk/subsurface.c index 383b936029..3a63fe44b9 100644 --- a/testsuite/gdk/subsurface.c +++ b/testsuite/gdk/subsurface.c @@ -47,11 +47,11 @@ test_subsurface_basics (void) 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 (sub, &rect); + gdk_subsurface_get_source_rect (sub, &rect); g_assert_true (graphene_rect_equal (&rect, &TEXTURE_RECT (texture))); - gdk_subsurface_get_dest (sub, &rect); + 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 (sub, &rect); + 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); From a44598772d54562fc73c8c2ae542e58cb0086cd8 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sun, 14 Apr 2024 18:46:05 -0400 Subject: [PATCH 10/17] wayland: Use subsurface bounds for holes When punching a hole into the opaque region, use the subsurface bounds, which takes both the texture and the background into account. --- gdk/wayland/gdksurface-wayland.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) 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); From c97bbfdfb172d492c051ae906b530aad05480ed7 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sun, 14 Apr 2024 18:46:55 -0400 Subject: [PATCH 11/17] offload: Use subsurface bounds for diffing When adding the whole subsurface to the diff, use the subsurface bounds, which takes both the texture and the background into account. --- gsk/gskoffload.c | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/gsk/gskoffload.c b/gsk/gskoffload.c index 226ab7d85c..e0dc27b23b 100644 --- a/gsk/gskoffload.c +++ b/gsk/gskoffload.c @@ -722,11 +722,10 @@ 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_background; + graphene_rect_t old_bounds; + graphene_rect_t bounds; - gdk_subsurface_get_texture_rect (info->subsurface, &old_dest); - gdk_subsurface_get_background_rect (info->subsurface, &old_background); + gdk_subsurface_get_bounds (info->subsurface, &old_bounds); if (info->can_offload) { @@ -764,27 +763,23 @@ 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) || - !gsk_rect_equal (&info->bg, &old_background)))) + (info->is_offloaded && !gsk_rect_equal (&bounds, &old_bounds))) { /* We changed things, need to invalidate everything */ cairo_rectangle_int_t rect; if (info->is_offloaded) { - gsk_rect_to_cairo_grow (&info->dest, &rect); - cairo_region_union_rectangle (diff, &rect); - gsk_rect_to_cairo_grow (&info->bg, &rect); + gsk_rect_to_cairo_grow (&bounds, &rect); cairo_region_union_rectangle (diff, &rect); } if (info->was_offloaded) { - gsk_rect_to_cairo_grow (&old_dest, &rect); - cairo_region_union_rectangle (diff, &rect); - gsk_rect_to_cairo_grow (&old_background, &rect); + gsk_rect_to_cairo_grow (&old_bounds, &rect); cairo_region_union_rectangle (diff, &rect); } } From 4aac64edf077bd6edf26cfbef02a6011c994795d Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sun, 14 Apr 2024 19:50:20 -0400 Subject: [PATCH 12/17] offload: Some renaming Rename things to be more in line with the subsurface api. --- gsk/gskoffload.c | 54 ++++++++++++++++++++--------------------- gsk/gskoffloadprivate.h | 9 ++++--- testsuite/gsk/offload.c | 18 +++++++++----- 3 files changed, 45 insertions(+), 36 deletions(-) diff --git a/gsk/gskoffload.c b/gsk/gskoffload.c index e0dc27b23b..79966d2179 100644 --- a/gsk/gskoffload.c +++ b/gsk/gskoffload.c @@ -83,8 +83,8 @@ find_texture_transform (GskTransform *transform) static GdkTexture * find_texture_to_attach (GskOffload *self, const GskRenderNode *subsurface_node, - graphene_rect_t *out_bounds, - graphene_rect_t *out_clip, + graphene_rect_t *out_texture_rect, + graphene_rect_t *out_source_rect, gboolean *has_background, GdkTextureTransform *out_texture_transform) { @@ -186,7 +186,7 @@ find_texture_to_attach (GskOffload *self, } else { - gsk_transform_transform_bounds (transform, &node->bounds, out_bounds); + gsk_transform_transform_bounds (transform, &node->bounds, out_texture_rect); clip = *c; has_clip = TRUE; } @@ -210,18 +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 { - gsk_transform_transform_bounds (transform, &node->bounds, out_bounds); - 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; @@ -479,8 +479,8 @@ visit_node (GskOffload *self, if (info->can_raise) { - if (gsk_rect_intersects (&transformed_bounds, &info->dest) || - gsk_rect_intersects (&transformed_bounds, &info->bg)) + if (gsk_rect_intersects (&transformed_bounds, &info->texture_rect) || + gsk_rect_intersects (&transformed_bounds, &info->background_rect)) { GskRenderNodeType type = GSK_RENDER_NODE_TYPE (node); @@ -653,17 +653,14 @@ complex_clip: { gboolean has_background; - info->texture = find_texture_to_attach (self, node, &info->dest, &info->source, &has_background, &info->transform); + 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, &info->dest, &info->dest); - if (has_background) - transform_bounds (self, &node->bounds, &info->bg); - else - graphene_rect_init (&info->bg, 0, 0, 0, 0); - + 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; } @@ -703,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) @@ -732,18 +732,18 @@ gsk_offload_new (GdkSurface *surface, 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->bg, + 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->bg, + info->has_background ? &info->background_rect : NULL, info->place_above != NULL, info->place_above); } diff --git a/gsk/gskoffloadprivate.h b/gsk/gskoffloadprivate.h index ec70ae692a..6848d83436 100644 --- a/gsk/gskoffloadprivate.h +++ b/gsk/gskoffloadprivate.h @@ -31,10 +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 bg; + graphene_rect_t background_rect; guint was_offloaded : 1; guint can_offload : 1; @@ -43,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/testsuite/gsk/offload.c b/testsuite/gsk/offload.c index 3e2b186411..88949716c5 100644 --- a/testsuite/gsk/offload.c +++ b/testsuite/gsk/offload.c @@ -210,11 +210,11 @@ 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); + 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\n", - info->dest.origin.x, info->dest.origin.y, - info->dest.size.width, info->dest.size.height); + info->texture_rect.origin.x, info->texture_rect.origin.y, + info->texture_rect.size.width, info->texture_rect.size.height); } else g_string_append_printf (s, "%u: %snot offloaded\n", @@ -416,8 +416,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 +455,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; } From 5c892fa4eab6557c9b757f1383927cf3ab16e124 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sun, 14 Apr 2024 20:53:57 -0400 Subject: [PATCH 13/17] offload: Add more tests Include background in offload tests. --- testsuite/gsk/meson.build | 6 +- testsuite/gsk/offload.c | 7 +- testsuite/gsk/offload/background.node | 71 +++++++++++++++++++ testsuite/gsk/offload/background.offload | 4 ++ testsuite/gsk/offload/background2.diff | 1 + testsuite/gsk/offload/background2.node | 15 ++++ testsuite/gsk/offload/background2.node2 | 22 ++++++ testsuite/gsk/offload/background2.offload | 1 + testsuite/gsk/offload/background2.offload2 | 1 + testsuite/gsk/offload/move.offload2 | 2 +- testsuite/gsk/offload/start_offloading.diff | 1 - .../gsk/offload/stop_offloading.offload2 | 2 +- 12 files changed, 127 insertions(+), 6 deletions(-) create mode 100644 testsuite/gsk/offload/background.node create mode 100644 testsuite/gsk/offload/background.offload create mode 100644 testsuite/gsk/offload/background2.diff create mode 100644 testsuite/gsk/offload/background2.node create mode 100644 testsuite/gsk/offload/background2.node2 create mode 100644 testsuite/gsk/offload/background2.offload create mode 100644 testsuite/gsk/offload/background2.offload2 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 88949716c5..03c9611d70 100644 --- a/testsuite/gsk/offload.c +++ b/testsuite/gsk/offload.c @@ -212,9 +212,14 @@ collect_offload_info (GdkSurface *surface, g_string_append_printf (s, "source: %g %g %g %g, ", 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\n", + 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", 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/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 From d563d158c0cdf779c28fb8599fa804a00c65c3ca Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sun, 14 Apr 2024 21:53:05 -0400 Subject: [PATCH 14/17] subsurface: Add gdk_subsurface_get_sibling This was just a missing getter. The backend implementation will use this information to determine whether stacking changes. --- gdk/gdksubsurface.c | 22 ++++++++++++++++++++++ gdk/gdksubsurfaceprivate.h | 3 ++- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/gdk/gdksubsurface.c b/gdk/gdksubsurface.c index 128a7f26d7..0a36826459 100644 --- a/gdk/gdksubsurface.c +++ b/gdk/gdksubsurface.c @@ -286,6 +286,28 @@ gdk_subsurface_is_above_parent (GdkSubsurface *subsurface) 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` diff --git a/gdk/gdksubsurfaceprivate.h b/gdk/gdksubsurfaceprivate.h index c588676e6c..6a28ac3f1d 100644 --- a/gdk/gdksubsurfaceprivate.h +++ b/gdk/gdksubsurfaceprivate.h @@ -101,6 +101,8 @@ void gdk_subsurface_get_source_rect (GdkSubsurface *subsu 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); gboolean gdk_subsurface_get_background_rect (GdkSubsurface *subsurface, @@ -108,6 +110,5 @@ gboolean gdk_subsurface_get_background_rect (GdkSubsurface *subsu void gdk_subsurface_get_bounds (GdkSubsurface *subsurface, graphene_rect_t *bounds); - G_END_DECLS From f75b4aac1dc2dd6d3865c564a69e277bbab1c27e Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sun, 14 Apr 2024 22:02:38 -0400 Subject: [PATCH 15/17] wayland: Minimize work for subsurfaces Only commit things that have changed. In the ideal scenario, only the texture changes from frame to frame, and all the sizing related setup and the background stay the same, causing the least amount of work in the compositor. --- gdk/wayland/gdksubsurface-wayland.c | 178 ++++++++++++++++++++-------- 1 file changed, 126 insertions(+), 52 deletions(-) diff --git a/gdk/wayland/gdksubsurface-wayland.c b/gdk/wayland/gdksubsurface-wayland.c index 8d05e06995..1cf5fa4cf1 100644 --- a/gdk/wayland/gdksubsurface-wayland.c +++ b/gdk/wayland/gdksubsurface-wayland.c @@ -318,6 +318,14 @@ gdk_wayland_subsurface_attach (GdkSubsurface *sub, double scale; graphene_rect_t device_rect; 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; @@ -330,22 +338,56 @@ 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); 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; @@ -353,6 +395,7 @@ gdk_wayland_subsurface_attach (GdkSubsurface *sub, } else { + background_changed = self->bg_attached; self->bg_rect.x = 0; self->bg_rect.y = 0; self->bg_rect.width = 0; @@ -361,6 +404,9 @@ gdk_wayland_subsurface_attach (GdkSubsurface *sub, 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 || self->dest.width != dest->size.width || @@ -470,14 +516,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) { @@ -486,38 +546,39 @@ gdk_wayland_subsurface_attach (GdkSubsurface *sub, 0, 0, gdk_texture_get_width (texture), gdk_texture_get_height (texture)); - + needs_commit = TRUE; } if (has_background) { - ensure_bg_surface (self); - - if (self->bg_surface) + 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)); - - if (!self->bg_attached) - { - self->bg_attached = TRUE; - - wl_surface_attach (self->bg_surface, get_sp_buffer (self), 0, 0); - wl_surface_damage_buffer (self->bg_surface, 0, 0, 1, 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_surface && self->bg_attached) + if (self->bg_attached) { self->bg_attached = FALSE; wl_surface_attach (self->bg_surface, NULL, 0, 0); + needs_bg_commit = TRUE; } } @@ -525,42 +586,55 @@ gdk_wayland_subsurface_attach (GdkSubsurface *sub, } 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_surface) + 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; + } } - if (self->bg_attached) - wl_subsurface_place_below (self->bg_subsurface, self->surface); + if (needs_commit) + wl_surface_commit (self->surface); - wl_surface_commit (self->surface); - if (self->bg_surface) + if (needs_bg_commit) wl_surface_commit (self->bg_surface); - ((GdkWaylandSurface *)sub->parent)->has_pending_subsurface_commits = TRUE; - GDK_WAYLAND_SURFACE (sub->parent)->opaque_region_dirty = TRUE; + ((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; } From 0c67b367ac3f7b6c61eb386d6a1dc2fca77fa5aa Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Mon, 15 Apr 2024 07:03:27 -0400 Subject: [PATCH 16/17] subsurface: Tweak the attach implementation Do the backend call before changing the stacking order in the frontend. This is necessary so the backend can look at the current stacking order to determine if it will change. --- gdk/gdksubsurface.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/gdk/gdksubsurface.c b/gdk/gdksubsurface.c index 0a36826459..20d4810a4c 100644 --- a/gdk/gdksubsurface.c +++ b/gdk/gdksubsurface.c @@ -150,6 +150,7 @@ gdk_subsurface_attach (GdkSubsurface *subsurface, 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); @@ -163,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) @@ -188,14 +198,7 @@ gdk_subsurface_attach (GdkSubsurface *subsurface, } } - return GDK_SUBSURFACE_GET_CLASS (subsurface)->attach (subsurface, - texture, - source, - dest, - transform, - background, - above, - sibling); + return result; } /*< private > From fbe000734a9d5d03f4a21fd6a81614208f86a46d Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Mon, 15 Apr 2024 11:11:39 -0400 Subject: [PATCH 17/17] wayland: Refuse offloading when we can't If we have a subsurface with background, but no single-pixel buffer support, refuse to offload. --- gdk/wayland/gdksubsurface-wayland.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/gdk/wayland/gdksubsurface-wayland.c b/gdk/wayland/gdksubsurface-wayland.c index 1cf5fa4cf1..eaf5b72e8b 100644 --- a/gdk/wayland/gdksubsurface-wayland.c +++ b/gdk/wayland/gdksubsurface-wayland.c @@ -311,6 +311,7 @@ gdk_wayland_subsurface_attach (GdkSubsurface *sub, { 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; @@ -457,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;