Merge branch 'dmabuf-glarea' into 'main'

One approach to dmabufs in glarea

See merge request GNOME/gtk!7116
This commit is contained in:
Matthias Clasen 2024-04-07 17:24:33 +00:00
commit f36f1b9829
13 changed files with 207 additions and 33 deletions

View File

@ -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);

View File

@ -25,6 +25,7 @@
#include "gdkdmabufformatsbuilderprivate.h"
#include "gdkdmabuffourccprivate.h"
#include "gdkdmabufprivate.h"
#include "gdkdmabuftexturebuilderprivate.h"
#include "gdktextureprivate.h"
#include <gdk/gdkglcontext.h>
#include <gdk/gdkgltexturebuilder.h>

View File

@ -25,6 +25,7 @@
#include "gdkdisplay.h"
#include "gdkenumtypes.h"
#include "gdkdmabuftextureprivate.h"
#include "gdkdmabuftexturebuilderprivate.h"
#include <cairo-gobject.h>
@ -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);
}
}

View File

@ -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

View File

@ -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,

View File

@ -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);
}

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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);
}

View File

@ -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;

View File

@ -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 <unistd.h>
#endif
#include <epoxy/gl.h>
@ -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.

View File

@ -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);