diff --git a/demos/gtk-demo/shadertoy.c b/demos/gtk-demo/shadertoy.c index cc76e80dfe..06417a6a79 100644 --- a/demos/gtk-demo/shadertoy.c +++ b/demos/gtk-demo/shadertoy.c @@ -120,7 +120,7 @@ create_shadertoy_window (GtkWidget *do_widget) gtk_box_append (GTK_BOX (box), aspect); shadertoy = new_shadertoy ("/shadertoy/alienplanet.glsl"); - gtk_aspect_frame_set_child (GTK_ASPECT_FRAME (aspect), shadertoy); + gtk_aspect_frame_set_child (GTK_ASPECT_FRAME (aspect), gtk_graphics_offload_new (shadertoy)); sw = gtk_scrolled_window_new (); gtk_scrolled_window_set_min_content_height (GTK_SCROLLED_WINDOW (sw), 250); diff --git a/gdk/gdkdmabuftexture.c b/gdk/gdkdmabuftexture.c index a13b4077b5..324645b24c 100644 --- a/gdk/gdkdmabuftexture.c +++ b/gdk/gdkdmabuftexture.c @@ -25,6 +25,7 @@ #include "gdkdmabufformatsbuilderprivate.h" #include "gdkdmabuffourccprivate.h" #include "gdkdmabufprivate.h" +#include "gdkdmabuftexturebuilderprivate.h" #include "gdktextureprivate.h" #include #include diff --git a/gdk/gdkdmabuftexturebuilder.c b/gdk/gdkdmabuftexturebuilder.c index 5cb71c71b6..6508d9c2cf 100644 --- a/gdk/gdkdmabuftexturebuilder.c +++ b/gdk/gdkdmabuftexturebuilder.c @@ -25,6 +25,7 @@ #include "gdkdisplay.h" #include "gdkenumtypes.h" #include "gdkdmabuftextureprivate.h" +#include "gdkdmabuftexturebuilderprivate.h" #include @@ -948,22 +949,20 @@ gdk_dmabuf_texture_builder_set_update_region (GdkDmabufTextureBuilder *self, * * Builds a new `GdkTexture` with the values set up in the builder. * - * It is a programming error to call this function if any mandatory - * property has not been set. + * It is a programming error to call this function if any mandatory property has not been set. * - * If the dmabuf is not supported by GTK, %NULL will be returned and @error will be set. + * Not all formats defined in the `drm_fourcc.h` header are supported. You can use + * [method@Gdk.Display.get_dmabuf_formats] to get a list of supported formats. If the + * format is not supported by GTK, %NULL will be returned and @error will be set. * * The `destroy` function gets called when the returned texture gets released. * - * It is possible to call this function multiple times to create multiple textures, - * possibly with changing properties in between. - * * It is the responsibility of the caller to keep the file descriptors for the planes * open until the created texture is no longer used, and close them afterwards (possibly * using the @destroy notify). * - * Not all formats defined in the `drm_fourcc.h` header are supported. You can use - * [method@Gdk.Display.get_dmabuf_formats] to get a list of supported formats. + * It is possible to call this function multiple times to create multiple textures, + * possibly with changing properties in between. * * Returns: (transfer full) (nullable): a newly built `GdkTexture` or `NULL` * if the format is not supported @@ -1004,3 +1003,19 @@ gdk_dmabuf_texture_builder_get_dmabuf (GdkDmabufTextureBuilder *self) { return &self->dmabuf; } + +void +gdk_dmabuf_texture_builder_set_dmabuf (GdkDmabufTextureBuilder *self, + const GdkDmabuf *dmabuf) +{ + gdk_dmabuf_texture_builder_set_fourcc (self, dmabuf->fourcc); + gdk_dmabuf_texture_builder_set_modifier (self, dmabuf->modifier); + gdk_dmabuf_texture_builder_set_n_planes (self, dmabuf->n_planes); + + for (unsigned int i = 0; i < dmabuf->n_planes; i++) + { + gdk_dmabuf_texture_builder_set_fd (self, i, dmabuf->planes[i].fd); + gdk_dmabuf_texture_builder_set_stride (self, i, dmabuf->planes[i].stride); + gdk_dmabuf_texture_builder_set_offset (self, i, dmabuf->planes[i].offset); + } +} diff --git a/gdk/gdkdmabuftexturebuilderprivate.h b/gdk/gdkdmabuftexturebuilderprivate.h new file mode 100644 index 0000000000..fe725051fc --- /dev/null +++ b/gdk/gdkdmabuftexturebuilderprivate.h @@ -0,0 +1,14 @@ +#pragma once + +#include "gdkdmabuftexturebuilder.h" +#include "gdkdmabufprivate.h" + +G_BEGIN_DECLS + +const GdkDmabuf * gdk_dmabuf_texture_builder_get_dmabuf (GdkDmabufTextureBuilder *builder); + +void gdk_dmabuf_texture_builder_set_dmabuf (GdkDmabufTextureBuilder *builder, + const GdkDmabuf *dmabuf); + +G_END_DECLS + diff --git a/gdk/gdkdmabuftextureprivate.h b/gdk/gdkdmabuftextureprivate.h index fb6ebc9916..074066a2af 100644 --- a/gdk/gdkdmabuftextureprivate.h +++ b/gdk/gdkdmabuftextureprivate.h @@ -7,8 +7,6 @@ G_BEGIN_DECLS -const GdkDmabuf * gdk_dmabuf_texture_builder_get_dmabuf (GdkDmabufTextureBuilder *builder); - GdkTexture * gdk_dmabuf_texture_new_from_builder (GdkDmabufTextureBuilder *builder, GDestroyNotify destroy, gpointer data, diff --git a/gdk/gdksubsurface.c b/gdk/gdksubsurface.c index 13776a0bfa..5f0a26f62e 100644 --- a/gdk/gdksubsurface.c +++ b/gdk/gdksubsurface.c @@ -113,6 +113,7 @@ gdk_subsurface_attach (GdkSubsurface *subsurface, GdkTexture *texture, const graphene_rect_t *source, const graphene_rect_t *dest, + GdkTextureTransform transform, gboolean above, GdkSubsurface *sibling) { @@ -155,7 +156,7 @@ gdk_subsurface_attach (GdkSubsurface *subsurface, } } - return GDK_SUBSURFACE_GET_CLASS (subsurface)->attach (subsurface, texture, source, dest, above, sibling); + return GDK_SUBSURFACE_GET_CLASS (subsurface)->attach (subsurface, texture, source, dest, transform, above, sibling); } void @@ -203,3 +204,12 @@ gdk_subsurface_is_above_parent (GdkSubsurface *subsurface) 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/gdksubsurfaceprivate.h b/gdk/gdksubsurfaceprivate.h index 2ffe394e5b..ea36af1bf5 100644 --- a/gdk/gdksubsurfaceprivate.h +++ b/gdk/gdksubsurfaceprivate.h @@ -47,6 +47,16 @@ struct _GdkSubsurface 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 { @@ -56,6 +66,7 @@ struct _GdkSubsurfaceClass GdkTexture *texture, const graphene_rect_t *source, const graphene_rect_t *dest, + GdkTextureTransform transform, gboolean above, GdkSubsurface *sibling); void (* detach) (GdkSubsurface *subsurface); @@ -64,15 +75,19 @@ struct _GdkSubsurfaceClass 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, gboolean above, GdkSubsurface *sibling); void gdk_subsurface_detach (GdkSubsurface *subsurface); @@ -82,6 +97,8 @@ void gdk_subsurface_get_source (GdkSubsurface *subsurfac 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 42f357e89f..fb5c98a85a 100644 --- a/gdk/wayland/gdksubsurface-wayland-private.h +++ b/gdk/wayland/gdksubsurface-wayland-private.h @@ -2,6 +2,7 @@ #include "gdksubsurfaceprivate.h" +#include "wayland-client-protocol.h" typedef struct _GdkWaylandSubsurface GdkWaylandSubsurface; typedef struct _GdkWaylandSubsurfaceClass GdkWaylandSubsurfaceClass; @@ -23,6 +24,7 @@ struct _GdkWaylandSubsurface GdkTexture *texture; cairo_rectangle_int_t dest; graphene_rect_t source; + enum wl_output_transform transform; struct wl_region *opaque_region; diff --git a/gdk/wayland/gdksubsurface-wayland.c b/gdk/wayland/gdksubsurface-wayland.c index 588974812d..3e72234e85 100644 --- a/gdk/wayland/gdksubsurface-wayland.c +++ b/gdk/wayland/gdksubsurface-wayland.c @@ -149,11 +149,24 @@ get_wl_buffer (GdkWaylandSubsurface *self, return buffer; } +static inline enum wl_output_transform +gdk_texture_transform_to_wl (GdkTextureTransform transform) +{ + return (enum wl_output_transform) transform; +} + +static inline GdkTextureTransform +wl_output_transform_to_gdk (enum wl_output_transform transform) +{ + return (GdkTextureTransform) transform; +} + static gboolean gdk_wayland_subsurface_attach (GdkSubsurface *sub, GdkTexture *texture, const graphene_rect_t *source, const graphene_rect_t *dest, + GdkTextureTransform transform, gboolean above, GdkSubsurface *sibling) { @@ -188,6 +201,8 @@ gdk_wayland_subsurface_attach (GdkSubsurface *sub, self->source.size.width = source->size.width; self->source.size.height = source->size.height; + self->transform = gdk_texture_transform_to_wl (transform); + scale = gdk_fractional_scale_to_double (&parent->scale); device_rect.origin.x = dest->origin.x * scale; @@ -302,6 +317,7 @@ 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, @@ -406,6 +422,14 @@ gdk_wayland_subsurface_get_source (GdkSubsurface *sub, source->size.height = self->source.size.height; } +static GdkTextureTransform +gdk_wayland_subsurface_get_transform (GdkSubsurface *sub) +{ + GdkWaylandSubsurface *self = GDK_WAYLAND_SUBSURFACE (sub); + + return wl_output_transform_to_gdk (self->transform); +} + static void gdk_wayland_subsurface_class_init (GdkWaylandSubsurfaceClass *class) { @@ -419,6 +443,7 @@ gdk_wayland_subsurface_class_init (GdkWaylandSubsurfaceClass *class) 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_transform = gdk_wayland_subsurface_get_transform; }; static void diff --git a/gsk/gskoffload.c b/gsk/gskoffload.c index f4fc9558e7..0ef9323a7c 100644 --- a/gsk/gskoffload.c +++ b/gsk/gskoffload.c @@ -55,18 +55,46 @@ struct _GskOffload GskOffloadInfo *last_info; }; +static GdkTextureTransform +find_texture_transform (GskTransform *transform) +{ + float sx, sy, dx, dy; + + g_assert (gsk_transform_get_category (transform) >= GSK_TRANSFORM_CATEGORY_2D_AFFINE); + + gsk_transform_to_affine (transform, &sx, &sy, &dx, &dy); + + if (sx > 0) + { + if (sy > 0) + return GDK_TEXTURE_TRANSFORM_NORMAL; + else + return GDK_TEXTURE_TRANSFORM_FLIPPED_180; + } + else + { + if (sy > 0) + return GDK_TEXTURE_TRANSFORM_FLIPPED; + else + return GDK_TEXTURE_TRANSFORM_180; + } +} + static GdkTexture * find_texture_to_attach (GskOffload *self, GdkSubsurface *subsurface, const GskRenderNode *node, - graphene_rect_t *out_clip) + graphene_rect_t *out_clip, + GdkTextureTransform *out_texture_transform) { gboolean has_clip = FALSE; graphene_rect_t clip; + GskTransform *transform = NULL; + GdkTexture *ret = NULL; for (;;) { - switch ((int)GSK_RENDER_NODE_TYPE (node)) + switch ((int) GSK_RENDER_NODE_TYPE (node)) { case GSK_DEBUG_NODE: node = gsk_debug_node_get_child (node); @@ -78,7 +106,7 @@ find_texture_to_attach (GskOffload *self, 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)); - return NULL; + goto out; } node = gsk_container_node_get_child (node, 0); break; @@ -94,7 +122,7 @@ find_texture_to_attach (GskOffload *self, "Can't offload subsurface %p: transform %s is not just scale/translate", subsurface, s); g_free (s); - return NULL; + goto out; } if (has_clip) @@ -104,6 +132,8 @@ find_texture_to_attach (GskOffload *self, gsk_transform_unref (inv); } + transform = gsk_transform_transform (transform, gsk_transform_ref (t)); + node = gsk_transform_node_get_child (node); } break; @@ -118,7 +148,7 @@ find_texture_to_attach (GskOffload *self, { GDK_DISPLAY_DEBUG (gdk_surface_get_display (self->surface), OFFLOAD, "Can't offload subsurface %p: empty clip", subsurface); - return NULL; + goto out; } } else @@ -135,6 +165,8 @@ find_texture_to_attach (GskOffload *self, { GdkTexture *texture = gsk_texture_node_get_texture (node); + *out_texture_transform = find_texture_transform (transform); + if (has_clip) { float dx = node->bounds.origin.x; @@ -157,16 +189,22 @@ find_texture_to_attach (GskOffload *self, out_clip->size.height = gdk_texture_get_height (texture); } - return texture; + ret = texture; + goto out; } default: GDK_DISPLAY_DEBUG (gdk_surface_get_display (self->surface), OFFLOAD, "Can't offload subsurface %p: Only textures supported but found %s", subsurface, g_type_name_from_instance ((GTypeInstance *) node)); - return NULL; + goto out; } } + +out: + g_clear_pointer (&transform, gsk_transform_unref); + + return ret; } static void @@ -551,9 +589,12 @@ complex_clip: case GSK_SUBSURFACE_NODE: { GdkSubsurface *subsurface = gsk_subsurface_node_get_subsurface (node); + GskTransform *transform; GskOffloadInfo *info = find_subsurface_info (self, subsurface); + transform = self->transforms ? (GskTransform *) self->transforms->data : NULL; + if (info == NULL) { GDK_DISPLAY_DEBUG (gdk_surface_get_display (self->surface), OFFLOAD, @@ -566,8 +607,7 @@ complex_clip: "Can't offload subsurface %p: clipped", subsurface); } - else if (self->transforms && - gsk_transform_get_category ((GskTransform *)self->transforms->data) < GSK_TRANSFORM_CATEGORY_2D_AFFINE) + else if (gsk_transform_get_category (transform) < GSK_TRANSFORM_CATEGORY_2D_AFFINE) { GDK_DISPLAY_DEBUG (gdk_surface_get_display (self->surface), OFFLOAD, "Can't offload subsurface %p: non-affine transform", @@ -575,14 +615,11 @@ complex_clip: } else { - graphene_rect_t clip; - - info->texture = find_texture_to_attach (self, subsurface, gsk_subsurface_node_get_child (node), &clip); + info->texture = find_texture_to_attach (self, subsurface, gsk_subsurface_node_get_child (node), &info->source, &info->transform); if (info->texture) { info->can_offload = TRUE; info->can_raise = TRUE; - info->source = clip; transform_bounds (self, &node->bounds, &info->dest); info->place_above = self->last_info ? self->last_info->subsurface : NULL; self->last_info = info; @@ -653,12 +690,14 @@ gsk_offload_new (GdkSurface *surface, info->texture, &info->source, &info->dest, + info->transform, TRUE, NULL); else info->is_offloaded = gdk_subsurface_attach (info->subsurface, info->texture, &info->source, &info->dest, + info->transform, info->place_above != NULL, info->place_above); } diff --git a/gsk/gskoffloadprivate.h b/gsk/gskoffloadprivate.h index 7eb0645cdc..c4c604179a 100644 --- a/gsk/gskoffloadprivate.h +++ b/gsk/gskoffloadprivate.h @@ -33,6 +33,7 @@ typedef struct GdkSubsurface *place_above; graphene_rect_t dest; graphene_rect_t source; + GdkTextureTransform transform; guint was_offloaded : 1; guint can_offload : 1; diff --git a/gtk/gtkglarea.c b/gtk/gtkglarea.c index 9271768243..9cd9d661cf 100644 --- a/gtk/gtkglarea.c +++ b/gtk/gtkglarea.c @@ -34,6 +34,12 @@ #include "gtkcssnodeprivate.h" #include "gdk/gdkgltextureprivate.h" #include "gdk/gdkglcontextprivate.h" +#include "gdk/gdkdmabuftexturebuilderprivate.h" +#include "gdk/gdkdmabuftextureprivate.h" + +#ifdef HAVE_UNISTD_H +#include +#endif #include @@ -715,6 +721,26 @@ release_texture (gpointer data) texture->holder = NULL; } +static void +release_dmabuf (const GdkDmabuf *dmabuf) +{ +#ifndef G_OS_WIN32 + for (unsigned int i = 0; i < dmabuf->n_planes; i++) + close (dmabuf->planes[i].fd); +#endif +} + +static void +release_dmabuf_texture (gpointer data) +{ + Texture *texture = data; + + release_dmabuf (gdk_dmabuf_texture_get_dmabuf (GDK_DMABUF_TEXTURE (texture->holder))); + g_object_set_data (G_OBJECT (texture->holder), "gltexture", NULL); + + texture->holder = NULL; +} + static void gtk_gl_area_snapshot (GtkWidget *widget, GtkSnapshot *snapshot) @@ -758,6 +784,8 @@ gtk_gl_area_snapshot (GtkWidget *widget, { Texture *texture; gpointer sync = NULL; + GdkTexture *gltexture; + GdkDmabuf dmabuf; if (priv->needs_render || priv->auto_render) { @@ -781,9 +809,33 @@ gtk_gl_area_snapshot (GtkWidget *widget, gdk_gl_texture_builder_set_sync (texture->builder, sync); - texture->holder = gdk_gl_texture_builder_build (texture->builder, - release_texture, - texture); + gltexture = gdk_gl_texture_builder_build (texture->builder, + release_texture, + texture); + + if (gdk_gl_context_export_dmabuf (priv->context, + gdk_gl_texture_builder_get_id (texture->builder), + &dmabuf)) + { + GdkDmabufTextureBuilder *builder = gdk_dmabuf_texture_builder_new (); + + gdk_dmabuf_texture_builder_set_display (builder, gdk_gl_context_get_display (priv->context)); + gdk_dmabuf_texture_builder_set_width (builder, gdk_texture_get_width (gltexture)); + gdk_dmabuf_texture_builder_set_height (builder, gdk_texture_get_height (gltexture)); + gdk_dmabuf_texture_builder_set_premultiplied (builder, TRUE); + gdk_dmabuf_texture_builder_set_dmabuf (builder, &dmabuf); + + texture->holder = gdk_dmabuf_texture_builder_build (builder, release_dmabuf_texture, texture, NULL); + + g_object_unref (builder); + + if (texture->holder != NULL) + g_object_set_data_full (G_OBJECT (texture->holder), "gltexture", gltexture, g_object_unref); + else + release_dmabuf (&dmabuf); + } + else + texture->holder = gltexture; /* Our texture is rendered by OpenGL, so it is upside down, * compared to what GSK expects, so flip it back. diff --git a/testsuite/gdk/subsurface.c b/testsuite/gdk/subsurface.c index a0a0d8251e..37657e8049 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), TRUE, NULL); - gdk_subsurface_attach (sub1, texture, &TEXTURE_RECT (texture), &GRAPHENE_RECT_INIT (0, 0, 10, 10), TRUE, NULL); - gdk_subsurface_attach (sub2, texture, &TEXTURE_RECT (texture), &GRAPHENE_RECT_INIT (0, 0, 10, 10), TRUE, NULL); + 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); 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), FALSE, NULL); + gdk_subsurface_attach (sub2, texture, &TEXTURE_RECT (texture), &GRAPHENE_RECT_INIT (0, 0, 10, 10), GDK_TEXTURE_TRANSFORM_NORMAL, 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), TRUE, sub2); + gdk_subsurface_attach (sub1, texture, &TEXTURE_RECT (texture), &GRAPHENE_RECT_INIT (0, 0, 10, 10), GDK_TEXTURE_TRANSFORM_NORMAL, TRUE, sub2); g_assert_true (surface->subsurfaces_below == sub1); g_assert_true (sub1->sibling_above == NULL);