diff --git a/gdk/gdkdisplay.c b/gdk/gdkdisplay.c index 75b0535a6af24e5967e8c2e109ed05f61ef33b7c..0a0f2ca1c02a499b99078f1a85d131ae32bb95af 100644 --- a/gdk/gdkdisplay.c +++ b/gdk/gdkdisplay.c @@ -2004,6 +2004,7 @@ gdk_display_init_dmabuf (GdkDisplay *self) #ifdef HAVE_EGL gdk_display_add_dmabuf_downloader (self, gdk_dmabuf_get_egl_downloader (self, builder)); + gdk_dmabuf_formats_builder_next_priority (builder); #endif gdk_dmabuf_formats_builder_add_formats (builder, @@ -2016,6 +2017,29 @@ gdk_display_init_dmabuf (GdkDisplay *self) GDK_DISPLAY_DEBUG (self, DMABUF, "Initialized support for %zu dmabuf formats", gdk_dmabuf_formats_get_n_formats (self->dmabuf_formats)); + +#ifdef G_ENABLE_DEBUG + if (GDK_DISPLAY_DEBUG_CHECK (self, DMABUF)) + { + for (gsize i = 0; i < gdk_dmabuf_formats_get_n_formats (self->dmabuf_formats); i++) + { + guint32 fourcc; + guint64 modifier; + + gdk_dmabuf_formats_get_format (self->dmabuf_formats, i, &fourcc, &modifier); + + gdk_debug_message (" %.4s:%#" G_GINT64_MODIFIER "x%s", + (char *) &fourcc, + modifier, + gdk_dmabuf_formats_contains (self->egl_external_formats, fourcc, modifier) ? " (external)" : ""); + + if (i + 1 < gdk_dmabuf_formats_get_n_formats (self->dmabuf_formats) && + gdk_dmabuf_formats_next_priority (self->dmabuf_formats, i) == i + 1) + gdk_debug_message ("------"); + + } + } +#endif } /** diff --git a/gdk/gdkdmabuf.c b/gdk/gdkdmabuf.c index 669793441638bce1dd73f558a609e4f2bc966536..9f0c8bd65c457f97c8546f40c2e37a5dcecd3183 100644 --- a/gdk/gdkdmabuf.c +++ b/gdk/gdkdmabuf.c @@ -1965,10 +1965,6 @@ gdk_dmabuf_get_mmap_formats (void) if (!supported_formats[i].download) continue; - GDK_DEBUG (DMABUF, - "mmap dmabuf format %.4s:%#0" G_GINT64_MODIFIER "x", - (char *) &supported_formats[i].fourcc, (guint64) DRM_FORMAT_MOD_LINEAR); - gdk_dmabuf_formats_builder_add_format (builder, supported_formats[i].fourcc, DRM_FORMAT_MOD_LINEAR); diff --git a/gdk/gdkdmabufegl.c b/gdk/gdkdmabufegl.c index 96b17018f5197ad131d1dc8ad788d613ea9e87e8..ea3ccd784f1258fae557d96778e4de963c2462d5 100644 --- a/gdk/gdkdmabufegl.c +++ b/gdk/gdkdmabufegl.c @@ -93,24 +93,17 @@ gdk_dmabuf_egl_downloader_collect_formats (GdkDisplay *display, for (int j = 0; j < num_modifiers; j++) { - /* All linear formats we support are already added my the mmap downloader. - * We don't add external formats, unless we can use them (via GLES) - */ - if (modifiers[j] != DRM_FORMAT_MOD_LINEAR && - (!external_only[j] || gdk_gl_context_get_use_es (context))) + if (!external_only[j]) { - GDK_DISPLAY_DEBUG (display, DMABUF, - "%s EGL dmabuf format %.4s:%#" G_GINT64_MODIFIER "x", - external_only[j] ? "external " : "", - (char *) &fourccs[i], - modifiers[j]); - gdk_dmabuf_formats_builder_add_format (formats, fourccs[i], modifiers[j]); + all_external = FALSE; } - if (external_only[j]) - gdk_dmabuf_formats_builder_add_format (external, fourccs[i], modifiers[j]); else - all_external = FALSE; + { + if (gdk_gl_context_get_use_es (context)) + gdk_dmabuf_formats_builder_add_format (external, fourccs[i], modifiers[j]); + + } } /* Accept implicit modifiers as long as we accept the format at all. @@ -120,9 +113,9 @@ gdk_dmabuf_egl_downloader_collect_formats (GdkDisplay *display, * As an extra wrinkle, treat the implicit modifier as 'external only' * if all formats with the same fourcc are 'external only'. */ - if (!all_external || gdk_gl_context_get_use_es (context)) + if (!all_external) gdk_dmabuf_formats_builder_add_format (formats, fourccs[i], DRM_FORMAT_MOD_INVALID); - if (all_external) + else if (gdk_gl_context_get_use_es (context)) gdk_dmabuf_formats_builder_add_format (external, fourccs[i], DRM_FORMAT_MOD_INVALID); } @@ -149,6 +142,7 @@ GdkDmabufDownloader * gdk_dmabuf_get_egl_downloader (GdkDisplay *display, GdkDmabufFormatsBuilder *builder) { + GdkGLContext *context; GdkDmabufFormatsBuilder *formats; GdkDmabufFormatsBuilder *external; gboolean retval = FALSE; @@ -163,6 +157,8 @@ gdk_dmabuf_get_egl_downloader (GdkDisplay *display, return NULL; previous = gdk_gl_context_get_current (); + context = gdk_display_get_gl_context (display); + formats = gdk_dmabuf_formats_builder_new (); external = gdk_dmabuf_formats_builder_new (); @@ -172,6 +168,11 @@ gdk_dmabuf_get_egl_downloader (GdkDisplay *display, display->egl_external_formats = gdk_dmabuf_formats_builder_free_to_formats (external); gdk_dmabuf_formats_builder_add_formats (builder, display->egl_dmabuf_formats); + if (gdk_gl_context_get_use_es (context)) + { + gdk_dmabuf_formats_builder_next_priority (builder); + gdk_dmabuf_formats_builder_add_formats (builder, display->egl_external_formats); + } if (!retval) { diff --git a/gdk/gdkdmabufformats.c b/gdk/gdkdmabufformats.c index 3e45e22ff979edc1c2fe79da702f428f4cd34fc6..bdfd41b2b0944638b088ff733966fafd575fb5ba 100644 --- a/gdk/gdkdmabufformats.c +++ b/gdk/gdkdmabufformats.c @@ -49,14 +49,6 @@ * Since: 4.14 */ -struct _GdkDmabufFormats -{ - int ref_count; - - gsize n_formats; - GdkDmabufFormat *formats; -}; - G_DEFINE_BOXED_TYPE (GdkDmabufFormats, gdk_dmabuf_formats, gdk_dmabuf_formats_ref, gdk_dmabuf_formats_unref) /** @@ -152,7 +144,36 @@ gdk_dmabuf_formats_get_format (GdkDmabufFormats *formats, } /** - * gdk_dmabuf_formats_contains: + * gdk_dmabuf_formats_next_priority: + * @formats: a `GdkDmabufFormats` + * @idx: the index of the format to query +* + * Returns the index of the next-lower-priority format. + * + * The formats in a `GdkDmabufFormats` are sorted by decreasing + * priority. This function lets you identify formats with the + * same priority: all the formats between @idx and the return + * value of this function have the same priority. + * + * Returns: the index of the next lower priority format + * + * Since: 4.16 + */ +gsize +gdk_dmabuf_formats_next_priority (GdkDmabufFormats *formats, + gsize idx) +{ + GdkDmabufFormat *format; + + g_return_val_if_fail (idx < formats->n_formats, G_MAXSIZE); + + format = &formats->formats[idx]; + + return format->next_priority; +} + +/** + * gdk_dmabuf_format_contains: * @formats: a `GdkDmabufFormats` * @fourcc: a format code * @modifier: a format modifier @@ -184,6 +205,8 @@ gdk_dmabuf_formats_contains (GdkDmabufFormats *formats, * gdk_dmabuf_formats_new: * @formats: the formats * @n_formats: the length of @formats + * @device: the DRM device that the compositor uses, or + * 0 if this object doesn't describe compositor formats * * Creates a new `GdkDmabufFormats struct for * the given formats. @@ -197,7 +220,8 @@ gdk_dmabuf_formats_contains (GdkDmabufFormats *formats, */ GdkDmabufFormats * gdk_dmabuf_formats_new (GdkDmabufFormat *formats, - gsize n_formats) + gsize n_formats, + guint64 device) { GdkDmabufFormats *self; @@ -206,6 +230,7 @@ gdk_dmabuf_formats_new (GdkDmabufFormat *formats, self->ref_count = 1; self->n_formats = n_formats; self->formats = g_new (GdkDmabufFormat, n_formats); + self->device = device; memcpy (self->formats, formats, n_formats * sizeof (GdkDmabufFormat)); @@ -240,6 +265,9 @@ gdk_dmabuf_formats_equal (const GdkDmabufFormats *formats1, if (formats1 == NULL || formats2 == NULL) return FALSE; + if (formats1->device != formats2->device) + return FALSE; + if (formats1->n_formats != formats2->n_formats) return FALSE; @@ -249,7 +277,10 @@ gdk_dmabuf_formats_equal (const GdkDmabufFormats *formats1, GdkDmabufFormat *f2 = &formats2->formats[i]; if (f1->fourcc != f2->fourcc || - f1->modifier != f2->modifier) + f1->modifier != f2->modifier || + f1->next_priority != f2->next_priority || + f1->flags != f2->flags || + f1->device != f2->device) return FALSE; } diff --git a/gdk/gdkdmabufformats.h b/gdk/gdkdmabufformats.h index 0fef96ae83e8724e251017791c50ded29b694036..e214e423a4d07c1caa5ec570880eb2c6d90db50d 100644 --- a/gdk/gdkdmabufformats.h +++ b/gdk/gdkdmabufformats.h @@ -46,6 +46,10 @@ void gdk_dmabuf_formats_get_format (GdkDmabufFormats *formats guint32 *fourcc, guint64 *modifier); +GDK_AVAILABLE_IN_4_16 +gsize gdk_dmabuf_formats_next_priority (GdkDmabufFormats *formats, + gsize idx); + GDK_AVAILABLE_IN_4_14 gboolean gdk_dmabuf_formats_contains (GdkDmabufFormats *formats, guint32 fourcc, diff --git a/gdk/gdkdmabufformatsbuilder.c b/gdk/gdkdmabufformatsbuilder.c index 6d29bf71e2cd21d804260b65c06629f683b42263..109ed5532f83d41a0477777061d98a4782b83b8d 100644 --- a/gdk/gdkdmabufformatsbuilder.c +++ b/gdk/gdkdmabufformatsbuilder.c @@ -52,7 +52,9 @@ gdk_dmabuf_format_compare (gconstpointer data_a, const GdkDmabufFormat *a = data_a; const GdkDmabufFormat *b = data_b; - if (a->fourcc == b->fourcc) + if (a->next_priority != b->next_priority) + return (a->next_priority < b->next_priority) ? -1 : 1; + else if (a->fourcc == b->fourcc) return (a->modifier - b->modifier) >> 8 * (sizeof (gint64) - sizeof (gint)); else return a->fourcc - b->fourcc; @@ -67,7 +69,9 @@ gdk_dmabuf_format_equal (gconstpointer data_a, const GdkDmabufFormat *b = data_b; return a->fourcc == b->fourcc && - a->modifier == b->modifier; + a->flags == b->flags && + a->modifier == b->modifier && + a->device == b->device; } static void @@ -79,56 +83,80 @@ gdk_dmabuf_formats_builder_sort (GdkDmabufFormatsBuilder *self) gdk_dmabuf_format_compare); } -/* list must be sorted */ -static void -gdk_dmabuf_formats_builder_remove_duplicates (GdkDmabufFormatsBuilder *self) -{ - gsize i, j; - - for (i = 1, j = 0; i < gdk_dmabuf_formats_builder_get_size (self); i++) - { - if (gdk_dmabuf_format_equal (gdk_dmabuf_formats_builder_get (self, i), - gdk_dmabuf_formats_builder_get (self, j))) - continue; - - j++; - if (i != j) - *gdk_dmabuf_formats_builder_index (self, j) = *gdk_dmabuf_formats_builder_index (self, i); - } -} - GdkDmabufFormats * -gdk_dmabuf_formats_builder_free_to_formats (GdkDmabufFormatsBuilder *self) +gdk_dmabuf_formats_builder_free_to_formats_for_device (GdkDmabufFormatsBuilder *self, + guint64 device) { GdkDmabufFormats *formats; + gdk_dmabuf_formats_builder_next_priority (self); gdk_dmabuf_formats_builder_sort (self); - gdk_dmabuf_formats_builder_remove_duplicates (self); formats = gdk_dmabuf_formats_new (gdk_dmabuf_formats_builder_get_data (self), - gdk_dmabuf_formats_builder_get_size (self)); + gdk_dmabuf_formats_builder_get_size (self), + device); gdk_dmabuf_formats_builder_clear (self); g_free (self); return formats; } +GdkDmabufFormats * +gdk_dmabuf_formats_builder_free_to_formats (GdkDmabufFormatsBuilder *self) +{ + return gdk_dmabuf_formats_builder_free_to_formats_for_device (self, 0); +} + +void +gdk_dmabuf_formats_builder_add_format_for_device (GdkDmabufFormatsBuilder *self, + guint32 fourcc, + guint32 flags, + guint64 modifier, + guint64 device) +{ + GdkDmabufFormat format = { fourcc, flags, modifier, device, G_MAXSIZE }; + + for (gsize i = 0; i < gdk_dmabuf_formats_builder_get_size (self); i++) + { + if (gdk_dmabuf_format_equal (gdk_dmabuf_formats_builder_get (self, i), &format)) + return; + } + + gdk_dmabuf_formats_builder_append (self, &format); +} + void gdk_dmabuf_formats_builder_add_format (GdkDmabufFormatsBuilder *self, guint32 fourcc, guint64 modifier) { - gdk_dmabuf_formats_builder_append (self, &(GdkDmabufFormat) { fourcc, modifier }); + gdk_dmabuf_formats_builder_add_format_for_device (self, fourcc, 0, modifier, 0); +} + +void +gdk_dmabuf_formats_builder_next_priority (GdkDmabufFormatsBuilder *self) +{ + for (gsize i = gdk_dmabuf_formats_builder_get_size (self); i > 0; i--) + { + GdkDmabufFormat *format = gdk_dmabuf_formats_builder_get (self, i - 1); + + if (format->next_priority != G_MAXSIZE) + break; + + format->next_priority = gdk_dmabuf_formats_builder_get_size (self); + } } void gdk_dmabuf_formats_builder_add_formats (GdkDmabufFormatsBuilder *self, GdkDmabufFormats *formats) { - gdk_dmabuf_formats_builder_splice (self, - gdk_dmabuf_formats_builder_get_size (self), - 0, - FALSE, - gdk_dmabuf_formats_peek_formats (formats), - gdk_dmabuf_formats_get_n_formats (formats)); + for (gsize i = 0; i < gdk_dmabuf_formats_get_n_formats (formats); i++) + { + guint32 fourcc; + guint64 modifier; + + gdk_dmabuf_formats_get_format (formats, i, &fourcc, &modifier); + gdk_dmabuf_formats_builder_add_format (self, fourcc, modifier); + } } diff --git a/gdk/gdkdmabufformatsbuilderprivate.h b/gdk/gdkdmabufformatsbuilderprivate.h index e739f3f998a5315391ab8784e4775a8aac593e85..027000a89d9a8d00b587cf47cda955f07b5f42e4 100644 --- a/gdk/gdkdmabufformatsbuilderprivate.h +++ b/gdk/gdkdmabufformatsbuilderprivate.h @@ -4,11 +4,21 @@ typedef struct GdkDmabufFormatsBuilder GdkDmabufFormatsBuilder; -GdkDmabufFormatsBuilder * gdk_dmabuf_formats_builder_new (void); -GdkDmabufFormats * gdk_dmabuf_formats_builder_free_to_formats (GdkDmabufFormatsBuilder *self); - -void gdk_dmabuf_formats_builder_add_format (GdkDmabufFormatsBuilder *self, - guint32 fourcc, - guint64 modifier); -void gdk_dmabuf_formats_builder_add_formats (GdkDmabufFormatsBuilder *self, - GdkDmabufFormats *formats); +GdkDmabufFormatsBuilder *gdk_dmabuf_formats_builder_new (void); +GdkDmabufFormats * gdk_dmabuf_formats_builder_free_to_formats (GdkDmabufFormatsBuilder *self); + +void gdk_dmabuf_formats_builder_add_format (GdkDmabufFormatsBuilder *self, + guint32 fourcc, + guint64 modifier); + +GdkDmabufFormats * gdk_dmabuf_formats_builder_free_to_formats_for_device (GdkDmabufFormatsBuilder *self, + guint64 device); +void gdk_dmabuf_formats_builder_add_format_for_device (GdkDmabufFormatsBuilder *self, + guint32 fourcc, + guint32 flags, + guint64 modifier, + guint64 device); + +void gdk_dmabuf_formats_builder_next_priority (GdkDmabufFormatsBuilder *self); +void gdk_dmabuf_formats_builder_add_formats (GdkDmabufFormatsBuilder *self, + GdkDmabufFormats *formats); diff --git a/gdk/gdkdmabufformatsprivate.h b/gdk/gdkdmabufformatsprivate.h index 67c633dc1c461fbb6541e35412b69c94102d1e66..b9804861774de411a1091d7bae67e009f68a689e 100644 --- a/gdk/gdkdmabufformatsprivate.h +++ b/gdk/gdkdmabufformatsprivate.h @@ -6,10 +6,23 @@ typedef struct _GdkDmabufFormat GdkDmabufFormat; struct _GdkDmabufFormat { guint32 fourcc; + guint32 flags; guint64 modifier; + guint64 device; + gsize next_priority; +}; + +struct _GdkDmabufFormats +{ + int ref_count; + + gsize n_formats; + GdkDmabufFormat *formats; + guint64 device; }; GdkDmabufFormats * gdk_dmabuf_formats_new (GdkDmabufFormat *formats, - gsize n_formats); + gsize n_formats, + guint64 device); const GdkDmabufFormat * gdk_dmabuf_formats_peek_formats (GdkDmabufFormats *self); diff --git a/gdk/gdksubsurface.c b/gdk/gdksubsurface.c index 20d4810a4c75e37abe913ba5f1965df9fa494627..4c95aac34300a675aac18d20159486a7f571eb7f 100644 --- a/gdk/gdksubsurface.c +++ b/gdk/gdksubsurface.c @@ -21,6 +21,7 @@ #include "gdksurfaceprivate.h" #include "gdktexture.h" #include "gsk/gskrectprivate.h" +#include "gdkdebugprivate.h" G_DEFINE_TYPE (GdkSubsurface, gdk_subsurface, G_TYPE_OBJECT) @@ -36,6 +37,7 @@ gdk_subsurface_finalize (GObject *object) g_ptr_array_remove (subsurface->parent->subsurfaces, subsurface); g_clear_object (&subsurface->parent); + g_clear_pointer (&subsurface->dmabuf_formats, gdk_dmabuf_formats_unref); G_OBJECT_CLASS (gdk_subsurface_parent_class)->finalize (object); } @@ -116,6 +118,28 @@ insert_subsurface (GdkSubsurface *subsurface, } } +static inline gboolean +is_topmost_subsurface (GdkSubsurface *subsurface) +{ + GdkSurface *parent = subsurface->parent; + + return !subsurface->sibling_above && parent->subsurfaces_below != subsurface; +} + +static inline GdkSubsurface * +find_topmost_subsurface (GdkSurface *surface) +{ + GdkSubsurface *top = surface->subsurfaces_above; + + if (top) + { + while (top->sibling_above) + top = top->sibling_above; + } + + return top; +} + /*< private > * gdk_subsurface_attach: * @subsurface: the `GdkSubsurface` @@ -150,7 +174,7 @@ gdk_subsurface_attach (GdkSubsurface *subsurface, GdkSubsurface *sibling) { GdkSurface *parent = subsurface->parent; - gboolean result; + gboolean was_topmost, is_topmost, result; g_return_val_if_fail (GDK_IS_SUBSURFACE (subsurface), FALSE); g_return_val_if_fail (GDK_IS_TEXTURE (texture), FALSE); @@ -164,6 +188,8 @@ 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); + was_topmost = is_topmost_subsurface (subsurface); + result = GDK_SUBSURFACE_GET_CLASS (subsurface)->attach (subsurface, texture, source, @@ -198,6 +224,29 @@ gdk_subsurface_attach (GdkSubsurface *subsurface, } } + is_topmost = is_topmost_subsurface (subsurface); + + if (!was_topmost && is_topmost) + { + GDK_DISPLAY_DEBUG (parent->display, DMABUF, "Using formats of topmost subsurface %p", subsurface); + gdk_surface_set_effective_dmabuf_formats (parent, subsurface->dmabuf_formats); + } + else if (was_topmost && !is_topmost) + { + GdkSubsurface *top = find_topmost_subsurface (parent); + + if (top) + { + GDK_DISPLAY_DEBUG (parent->display, DMABUF, "Using formats of topmost subsurface %p", top); + gdk_surface_set_effective_dmabuf_formats (parent, top->dmabuf_formats); + } + else + { + GDK_DISPLAY_DEBUG (parent->display, DMABUF, "Using formats of main surface"); + gdk_surface_set_effective_dmabuf_formats (parent, parent->dmabuf_formats); + } + } + return result; } @@ -212,10 +261,25 @@ gdk_subsurface_attach (GdkSubsurface *subsurface, void gdk_subsurface_detach (GdkSubsurface *subsurface) { + gboolean was_topmost; + g_return_if_fail (GDK_IS_SUBSURFACE (subsurface)); + was_topmost = is_topmost_subsurface (subsurface); + remove_subsurface (subsurface); + if (was_topmost) + { + GdkSurface *parent = subsurface->parent; + GdkSubsurface *top = find_topmost_subsurface (parent); + + if (top) + gdk_surface_set_effective_dmabuf_formats (parent, top->dmabuf_formats); + else + gdk_surface_set_effective_dmabuf_formats (parent, parent->dmabuf_formats); + } + GDK_SUBSURFACE_GET_CLASS (subsurface)->detach (subsurface); } @@ -370,3 +434,17 @@ gdk_subsurface_get_bounds (GdkSubsurface *subsurface, if (gdk_subsurface_get_background_rect (subsurface, &background)) graphene_rect_union (bounds, &background, bounds); } + +void +gdk_subsurface_set_dmabuf_formats (GdkSubsurface *subsurface, + GdkDmabufFormats *formats) +{ + g_return_if_fail (GDK_IS_SUBSURFACE (subsurface)); + g_return_if_fail (formats != NULL); + + g_clear_pointer (&subsurface->dmabuf_formats, gdk_dmabuf_formats_unref); + subsurface->dmabuf_formats = gdk_dmabuf_formats_ref (formats); + + if (subsurface->parent && is_topmost_subsurface (subsurface)) + gdk_surface_set_effective_dmabuf_formats (subsurface->parent, formats); +} diff --git a/gdk/gdksubsurfaceprivate.h b/gdk/gdksubsurfaceprivate.h index 6a28ac3f1df09946051c9cf877fee13a655f0b22..27c4ed194c593a1d0915270be27c6ba15d01e7a3 100644 --- a/gdk/gdksubsurfaceprivate.h +++ b/gdk/gdksubsurfaceprivate.h @@ -21,6 +21,7 @@ #include "gdkenumtypes.h" #include "gdksurface.h" +#include "gdkdmabufformats.h" #include G_BEGIN_DECLS @@ -45,6 +46,8 @@ struct _GdkSubsurface gboolean above_parent; GdkSubsurface *sibling_above; GdkSubsurface *sibling_below; + + GdkDmabufFormats *dmabuf_formats; }; typedef enum { @@ -109,6 +112,8 @@ gboolean gdk_subsurface_get_background_rect (GdkSubsurface *subsu graphene_rect_t *rect); void gdk_subsurface_get_bounds (GdkSubsurface *subsurface, graphene_rect_t *bounds); +void gdk_subsurface_set_dmabuf_formats (GdkSubsurface *subsurface, + GdkDmabufFormats *formats); G_END_DECLS diff --git a/gdk/gdksurface.c b/gdk/gdksurface.c index 6876f8967ed2d79aa061c5115e5caf5944d40a8b..695a0fbab87d931d28e30946c4a33011d2c1b9f1 100644 --- a/gdk/gdksurface.c +++ b/gdk/gdksurface.c @@ -91,6 +91,7 @@ enum { PROP_0, PROP_CURSOR, PROP_DISPLAY, + PROP_DMABUF_FORMATS, PROP_FRAME_CLOCK, PROP_MAPPED, PROP_WIDTH, @@ -548,6 +549,18 @@ gdk_surface_class_init (GdkSurfaceClass *klass) GDK_TYPE_DISPLAY, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); + /** + * GdkSurface:dmabuf-formats: (attributes org.gtk.Property.get=gdk_surface_get_dmabuf_formats) + * + * The dmabuf formats that can be used with this surface. + * + * Since: 4.14 + */ + properties[PROP_DMABUF_FORMATS] = + g_param_spec_boxed ("dmabuf-formats", NULL, NULL, + GDK_TYPE_DMABUF_FORMATS, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + /** * GdkSurface:frame-clock: (attributes org.gtk.Property.get=gdk_surface_get_frame_clock) * @@ -773,6 +786,9 @@ gdk_surface_finalize (GObject *object) g_ptr_array_unref (surface->subsurfaces); + g_clear_pointer (&surface->dmabuf_formats, gdk_dmabuf_formats_unref); + g_clear_pointer (&surface->effective_dmabuf_formats, gdk_dmabuf_formats_unref); + G_OBJECT_CLASS (gdk_surface_parent_class)->finalize (object); } @@ -827,6 +843,10 @@ gdk_surface_get_property (GObject *object, g_value_set_object (value, surface->display); break; + case PROP_DMABUF_FORMATS: + g_value_set_boxed (value, surface->effective_dmabuf_formats); + break; + case PROP_FRAME_CLOCK: g_value_set_object (value, surface->frame_clock); break; @@ -3076,3 +3096,70 @@ gdk_surface_get_subsurface (GdkSurface *surface, { return g_ptr_array_index (surface->subsurfaces, idx); } + +void +gdk_surface_set_dmabuf_formats (GdkSurface *surface, + GdkDmabufFormats *formats) +{ + g_return_if_fail (GDK_IS_SURFACE (surface)); + g_return_if_fail (formats != NULL); + + g_clear_pointer (&surface->dmabuf_formats, gdk_dmabuf_formats_unref); + surface->dmabuf_formats = gdk_dmabuf_formats_ref (formats); + + if (surface->subsurfaces_above == NULL) + { + GDK_DISPLAY_DEBUG (surface->display, DMABUF, "Using main surface formats"); + gdk_surface_set_effective_dmabuf_formats (surface, formats); + } +} + +void +gdk_surface_set_effective_dmabuf_formats (GdkSurface *surface, + GdkDmabufFormats *formats) +{ + g_return_if_fail (GDK_IS_SURFACE (surface)); + + if (!formats) + return; + + if (gdk_dmabuf_formats_equal (surface->effective_dmabuf_formats, formats)) + { + GDK_DISPLAY_DEBUG (surface->display, DMABUF, "Formats unchanged"); + return; + } + + g_clear_pointer (&surface->effective_dmabuf_formats, gdk_dmabuf_formats_unref); + surface->effective_dmabuf_formats = gdk_dmabuf_formats_ref (formats); + + g_object_notify_by_pspec (G_OBJECT (surface), properties[PROP_DMABUF_FORMATS]); +} + +/** + * gdk_surface_get_dmabuf_formats: + * @surface: a `GdkSurface` + * + * Returns the dma-buf formats that are supported on this surface. + * + * What formats can be used may depend on the geometry and stacking + * order of the surface and its subsurfaces, and can change over time. + * + * The formats returned by this function can be used for negotiating + * buffer formats with producers such as v4l, pipewire or GStreamer. + * + * To learn more about dma-bufs, see [class@Gdk.DmabufTextureBuilder]. + * + * Returns: (transfer none): a `GdkDmabufFormats` object + * + * Since: 4.16 + */ +GdkDmabufFormats * +gdk_surface_get_dmabuf_formats (GdkSurface *surface) +{ + g_return_val_if_fail (GDK_IS_SURFACE (surface), NULL); + + if (surface->effective_dmabuf_formats) + return surface->effective_dmabuf_formats; + else + return gdk_display_get_dmabuf_formats (surface->display); +} diff --git a/gdk/gdksurface.h b/gdk/gdksurface.h index 5c8025c38f93182325e8cf8305b997fbc69cc5d9..b712754fa9e45b510dbe832a4363a63afc18d86c 100644 --- a/gdk/gdksurface.h +++ b/gdk/gdksurface.h @@ -139,6 +139,10 @@ GdkVulkanContext * gdk_surface_create_vulkan_context(GdkSurface *surface, GError **error); +GDK_AVAILABLE_IN_4_16 +GdkDmabufFormats * + gdk_surface_get_dmabuf_formats (GdkSurface *surface); + G_DEFINE_AUTOPTR_CLEANUP_FUNC (GdkSurface, g_object_unref) G_END_DECLS diff --git a/gdk/gdksurfaceprivate.h b/gdk/gdksurfaceprivate.h index d06ba31148b3a1c9879374c272737705643c5088..66cb60bdaa35b3f9f8b9bbd9b665ac1eae718996 100644 --- a/gdk/gdksurfaceprivate.h +++ b/gdk/gdksurfaceprivate.h @@ -105,6 +105,9 @@ struct _GdkSurface */ GdkSubsurface *subsurfaces_above; GdkSubsurface *subsurfaces_below; + + GdkDmabufFormats *dmabuf_formats; + GdkDmabufFormats *effective_dmabuf_formats; }; struct _GdkSurfaceClass @@ -355,4 +358,10 @@ gsize gdk_surface_get_n_subsurfaces (GdkSurface *surface); GdkSubsurface * gdk_surface_get_subsurface (GdkSurface *surface, gsize idx); +void gdk_surface_set_dmabuf_formats (GdkSurface *surface, + GdkDmabufFormats *formats); + +void gdk_surface_set_effective_dmabuf_formats (GdkSurface *surface, + GdkDmabufFormats *formats); + G_END_DECLS diff --git a/gdk/wayland/gdkdisplay-wayland.c b/gdk/wayland/gdkdisplay-wayland.c index 4e6f0d8727ae24f20d1ed6764a3431fd61def826..372c2c271a02a21d703b4aba9f68e755324b9b49 100644 --- a/gdk/wayland/gdkdisplay-wayland.c +++ b/gdk/wayland/gdkdisplay-wayland.c @@ -337,6 +337,16 @@ static void gdk_wayland_display_remove_output (GdkWaylandDisplay *display_wa static void gdk_wayland_display_init_xdg_output (GdkWaylandDisplay *display_wayland); static void gdk_wayland_display_get_xdg_output (GdkWaylandMonitor *monitor); +static void +dmabuf_formats_callback (gpointer data, + DmabufFormatsInfo *info) +{ + GdkWaylandDisplay *display_wayland = GDK_WAYLAND_DISPLAY (data); + + g_clear_pointer (&display_wayland->wayland_dmabuf_formats, gdk_dmabuf_formats_unref); + display_wayland->wayland_dmabuf_formats = gdk_dmabuf_formats_ref (info->formats); +} + static void gdk_registry_handle_global (void *data, struct wl_registry *registry, @@ -370,7 +380,10 @@ gdk_registry_handle_global (void *data, feedback = zwp_linux_dmabuf_v1_get_default_feedback (display_wayland->linux_dmabuf); display_wayland->dmabuf_formats_info = dmabuf_formats_info_new (GDK_DISPLAY (display_wayland), "default", - feedback); + NULL, + feedback, + dmabuf_formats_callback, + display_wayland); _gdk_wayland_display_async_roundtrip (display_wayland); } else if (strcmp (interface, "xdg_wm_base") == 0) @@ -744,6 +757,7 @@ gdk_wayland_display_dispose (GObject *object) 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->dmabuf_formats_info, dmabuf_formats_info_free); + g_clear_pointer (&display_wayland->wayland_dmabuf_formats, gdk_dmabuf_formats_unref); g_clear_pointer (&display_wayland->shm, wl_shm_destroy); g_clear_pointer (&display_wayland->wl_registry, wl_registry_destroy); diff --git a/gdk/wayland/gdkdisplay-wayland.h b/gdk/wayland/gdkdisplay-wayland.h index 3000c47f819b2f11a25b1fccda3fd9597d682b6e..7df3512bb08bd8d102d97337c2015a07632fd5ab 100644 --- a/gdk/wayland/gdkdisplay-wayland.h +++ b/gdk/wayland/gdkdisplay-wayland.h @@ -100,6 +100,7 @@ struct _GdkWaylandDisplay struct wl_shm *shm; struct zwp_linux_dmabuf_v1 *linux_dmabuf; DmabufFormatsInfo *dmabuf_formats_info; + GdkDmabufFormats *wayland_dmabuf_formats; struct xdg_wm_base *xdg_wm_base; struct zxdg_shell_v6 *zxdg_shell_v6; struct gtk_shell1 *gtk_shell; diff --git a/gdk/wayland/gdkdmabuf-wayland-private.h b/gdk/wayland/gdkdmabuf-wayland-private.h index 97cc938a3b1ded22ded226979d8c1c7ff302f310..9c5179094ee60f695b4485fcbd8004cebea217fd 100644 --- a/gdk/wayland/gdkdmabuf-wayland-private.h +++ b/gdk/wayland/gdkdmabuf-wayland-private.h @@ -54,24 +54,40 @@ typedef struct typedef struct DmabufFormatsInfo DmabufFormatsInfo; +typedef void (* DmabufFormatsUpdateCallback) (gpointer data, + DmabufFormatsInfo *formats); + struct DmabufFormatsInfo { GdkDisplay *display; char *name; struct zwp_linux_dmabuf_feedback_v1 *feedback; + DmabufFormatsUpdateCallback callback; + gpointer data; + gsize n_dmabuf_formats; DmabufFormat *dmabuf_format_table; DmabufFormats *dmabuf_formats; DmabufFormats *pending_dmabuf_formats; DmabufTranche *pending_tranche; + + GdkDmabufFormats *egl_formats; + GdkDmabufFormats *formats; }; DmabufFormatsInfo * dmabuf_formats_info_new (GdkDisplay *display, const char *name, - struct zwp_linux_dmabuf_feedback_v1 *feedback); + GdkDmabufFormats *egl_formats, + struct zwp_linux_dmabuf_feedback_v1 *feedback, + DmabufFormatsUpdateCallback callback, + gpointer data); void dmabuf_formats_info_free (DmabufFormatsInfo *info); +void dmabuf_formats_info_set_egl_formats + (DmabufFormatsInfo *info, + GdkDmabufFormats *egl_formats); + G_END_DECLS diff --git a/gdk/wayland/gdkdmabuf-wayland.c b/gdk/wayland/gdkdmabuf-wayland.c index 2b9020153b3076e0fd763b1f1137cb052cd0805a..a7462eddfb4becd77b7e9532d34cebf6a52f4251 100644 --- a/gdk/wayland/gdkdmabuf-wayland.c +++ b/gdk/wayland/gdkdmabuf-wayland.c @@ -1,6 +1,7 @@ #include "config.h" #include "gdkdmabuf-wayland-private.h" +#include "gdkwaylanddmabufformats.h" #include "gdkdebugprivate.h" #include "gdkdmabufformatsprivate.h" @@ -45,44 +46,116 @@ dmabuf_formats_free (DmabufFormats *formats) g_free (formats); } +static gboolean +is_in_tranche (GdkDmabufFormats *formats, + gsize idx, + guint32 fourcc, + guint64 modifier) +{ + gsize end; + guint32 f; + guint64 m; + + end = gdk_dmabuf_formats_next_priority (formats, idx); + for (gsize i = idx; i < end; i++) + { + gdk_dmabuf_formats_get_format (formats, i, &f, &m); + if (f == fourcc && m == modifier) + return TRUE; + } + + return FALSE; +} + +static void +gdk_wayland_dmabuf_formats_dump (GdkDmabufFormats *formats, + const char *name) +{ + gdk_debug_message ("Wayland %s dmabuf formats: (%lu entries)", name, formats->n_formats); + gdk_debug_message ("Main device: %u %u", + major (formats->device), + minor (formats->device)); + + gsize i = 0; + while (i < formats->n_formats) + { + GdkDmabufFormat *format = &formats->formats[i]; + gsize next_priority = format->next_priority; + + if (i > 0) + gdk_debug_message ("------"); + + gdk_debug_message ("Tranche target device: %u %u", + major (format->device), + minor (format->device)); + if (format->flags & ZWP_LINUX_DMABUF_FEEDBACK_V1_TRANCHE_FLAGS_SCANOUT) + gdk_debug_message ("Tranche is scanout"); + gdk_debug_message ("Tranche formats (%lu entries)", next_priority - i); + + for (; i < next_priority; i++) + { + format = &formats->formats[i]; + gdk_debug_message (" %.4s:%#" G_GINT64_MODIFIER "x", (char *) &format->fourcc, format->modifier); + } + } +} + static void update_dmabuf_formats (DmabufFormatsInfo *info) { + GdkDmabufFormatsBuilder *builder; + GdkDmabufFormats *egl_formats = info->egl_formats; DmabufFormats *formats = info->dmabuf_formats; - GDK_DISPLAY_DEBUG (info->display, MISC, - "dmabuf format table (%lu entries)", info->n_dmabuf_formats); - GDK_DISPLAY_DEBUG (info->display, MISC, - "dmabuf main device: %u %u", - major (formats->main_device), - minor (formats->main_device)); + builder = gdk_dmabuf_formats_builder_new (); for (gsize i = 0; i < formats->tranches->len; i++) { DmabufTranche *tranche = g_ptr_array_index (formats->tranches, i); - GDK_DISPLAY_DEBUG (info->display, MISC, - "dmabuf tranche target device: %u %u", - major (tranche->target_device), - minor (tranche->target_device)); - - GDK_DISPLAY_DEBUG (info->display, MISC, - "dmabuf%s tranche (%lu entries):", - tranche->flags & ZWP_LINUX_DMABUF_FEEDBACK_V1_TRANCHE_FLAGS_SCANOUT ? " scanout" : "", - tranche->n_formats); - - for (gsize j = 0; j < tranche->n_formats; j++) + if (egl_formats) + { + for (gsize k = 0; k < gdk_dmabuf_formats_get_n_formats (egl_formats); k = gdk_dmabuf_formats_next_priority (egl_formats, k)) + { + for (gsize j = 0; j < tranche->n_formats; j++) + { + if (is_in_tranche (egl_formats, k, + tranche->formats[j].fourcc, + tranche->formats[j].modifier)) + gdk_dmabuf_formats_builder_add_format_for_device (builder, + tranche->formats[j].fourcc, + tranche->flags, + tranche->formats[j].modifier, + tranche->target_device); + } + gdk_dmabuf_formats_builder_next_priority (builder); + } + } + else { - GDK_DISPLAY_DEBUG (info->display, MISC, - " %.4s:%#" G_GINT64_MODIFIER "x", - (char *) &(tranche->formats[j].fourcc), - tranche->formats[j].modifier); + for (gsize j = 0; j < tranche->n_formats; j++) + { + gdk_dmabuf_formats_builder_add_format_for_device (builder, + tranche->formats[j].fourcc, + tranche->flags, + tranche->formats[j].modifier, + tranche->target_device); + } + gdk_dmabuf_formats_builder_next_priority (builder); } } + + g_clear_pointer (&info->formats, gdk_dmabuf_formats_unref); + info->formats = gdk_dmabuf_formats_builder_free_to_formats_for_device (builder, formats->main_device); + + if (GDK_DISPLAY_DEBUG_CHECK (info->display, DMABUF)) + gdk_wayland_dmabuf_formats_dump (info->formats, info->name); + + info->callback (info->data, info); } static void -linux_dmabuf_done (void *data, +linux_dmabuf_done (void *data, struct zwp_linux_dmabuf_feedback_v1 *feedback) { DmabufFormatsInfo *info = data; @@ -96,10 +169,10 @@ linux_dmabuf_done (void *data, } static void -linux_dmabuf_format_table (void *data, +linux_dmabuf_format_table (void *data, struct zwp_linux_dmabuf_feedback_v1 *feedback, - int32_t fd, - uint32_t size) + int32_t fd, + uint32_t size) { DmabufFormatsInfo *info = data; @@ -111,9 +184,9 @@ linux_dmabuf_format_table (void *data, } static void -linux_dmabuf_main_device (void *data, +linux_dmabuf_main_device (void *data, struct zwp_linux_dmabuf_feedback_v1 *feedback, - struct wl_array *device) + struct wl_array *device) { DmabufFormatsInfo *info = data; dev_t dev; @@ -127,7 +200,7 @@ linux_dmabuf_main_device (void *data, } static void -linux_dmabuf_tranche_done (void *data, +linux_dmabuf_tranche_done (void *data, struct zwp_linux_dmabuf_feedback_v1 *feedback) { DmabufFormatsInfo *info = data; @@ -139,9 +212,9 @@ linux_dmabuf_tranche_done (void *data, } static void -linux_dmabuf_tranche_target_device (void *data, +linux_dmabuf_tranche_target_device (void *data, struct zwp_linux_dmabuf_feedback_v1 *feedback, - struct wl_array *device) + struct wl_array *device) { DmabufFormatsInfo *info = data; dev_t dev; @@ -158,9 +231,9 @@ linux_dmabuf_tranche_target_device (void *data, } static void -linux_dmabuf_tranche_formats (void *data, +linux_dmabuf_tranche_formats (void *data, struct zwp_linux_dmabuf_feedback_v1 *feedback, - struct wl_array *indices) + struct wl_array *indices) { DmabufFormatsInfo *info = data; DmabufTranche *tranche; @@ -181,9 +254,9 @@ linux_dmabuf_tranche_formats (void *data, } static void -linux_dmabuf_tranche_flags (void *data, +linux_dmabuf_tranche_flags (void *data, struct zwp_linux_dmabuf_feedback_v1 *feedback, - uint32_t flags) + uint32_t flags) { DmabufFormatsInfo *info = data; DmabufTranche *tranche; @@ -206,7 +279,10 @@ static const struct zwp_linux_dmabuf_feedback_v1_listener feedback_listener = { DmabufFormatsInfo * dmabuf_formats_info_new (GdkDisplay *display, const char *name, - struct zwp_linux_dmabuf_feedback_v1 *feedback) + GdkDmabufFormats *egl_formats, + struct zwp_linux_dmabuf_feedback_v1 *feedback, + DmabufFormatsUpdateCallback callback, + gpointer data) { DmabufFormatsInfo *info; @@ -214,11 +290,21 @@ dmabuf_formats_info_new (GdkDisplay *display, info->display = display; info->name = g_strdup (name); + if (egl_formats) + { + info->egl_formats = gdk_dmabuf_formats_ref (egl_formats); + info->formats = gdk_dmabuf_formats_ref (egl_formats); + } info->feedback = feedback; + info->callback = callback; + info->data = data; + if (info->feedback) zwp_linux_dmabuf_feedback_v1_add_listener (info->feedback, &feedback_listener, info); + else + info->callback (info->data, info); return info; } @@ -227,6 +313,8 @@ void dmabuf_formats_info_free (DmabufFormatsInfo *info) { g_free (info->name); + g_clear_pointer (&info->formats, gdk_dmabuf_formats_unref); + g_clear_pointer (&info->egl_formats, gdk_dmabuf_formats_unref); g_clear_pointer (&info->feedback, zwp_linux_dmabuf_feedback_v1_destroy); if (info->dmabuf_format_table) { @@ -239,3 +327,91 @@ dmabuf_formats_info_free (DmabufFormatsInfo *info) g_free (info); } + +void +dmabuf_formats_info_set_egl_formats (DmabufFormatsInfo *info, + GdkDmabufFormats *egl_formats) +{ + if (info->egl_formats) + return; + + info->egl_formats = gdk_dmabuf_formats_ref (egl_formats); + + if (info->dmabuf_formats) + update_dmabuf_formats (info); +} + +/** + * gdk_wayland_dmabuf_formats_get_main_device: + * @formats: a `GdkDmabufFormats` + * + * Returns the DRM device that the compositor uses for compositing. + * + * If this information isn't available (e.g. because @formats wasn't + * obtained from the compositor), then 0 is returned. + * + * Returns: the main DRM device that the compositor prefers + * + * Since: 4.16 + */ +dev_t +gdk_wayland_dmabuf_formats_get_main_device (GdkDmabufFormats *formats) +{ + return formats->device; +} + +/** + * gdk_wayland_dmabuf_formats_get_target_device: + * @formats: a `GdkDmabufFormats` + * @idx: the index of the format to return + * + * Returns the target DRM device that should be used for creating buffers + * with this format. + * + * If this information isn't available (e.g. because @formats wasn't + * obtained from the compositor), then 0 is returned. + * + * Returns: the target DRM device for this format + * + * Since: 4.16 + */ +dev_t +gdk_wayland_dmabuf_formats_get_target_device (GdkDmabufFormats *formats, + gsize idx) +{ + GdkDmabufFormat *format; + + g_return_val_if_fail (idx < formats->n_formats, 0); + + format = &formats->formats[idx]; + + return format->device; +} + +/** + * gdk_wayland_dmabuf_formats_is_scanout: + * @formats: a `GdkDmabufFormats` + * @idx: the index of the format to return + * + * Returns whether the compositor may use buffers with this + * format for scanout. + * + * If this information isn't available (e.g. because @formats wasn't + * obtained from the compositor), then `FALSE` is returned. + * + * Returns: whether the format will be used for scanout + * + * Since: 4.16 + */ +gboolean +gdk_wayland_dmabuf_formats_is_scanout (GdkDmabufFormats *formats, + gsize idx) +{ + GdkDmabufFormat *format; + + g_return_val_if_fail (idx < formats->n_formats, FALSE); + + format = &formats->formats[idx]; + + return (format->flags & ZWP_LINUX_DMABUF_FEEDBACK_V1_TRANCHE_FLAGS_SCANOUT) != 0; +} diff --git a/gdk/wayland/gdksubsurface-wayland-private.h b/gdk/wayland/gdksubsurface-wayland-private.h index 9b8378ccc2d8af27415f4f0261c2018fee13df60..345ad597c24729ba9bb3a3e709668370d6f213da 100644 --- a/gdk/wayland/gdksubsurface-wayland-private.h +++ b/gdk/wayland/gdksubsurface-wayland-private.h @@ -1,6 +1,8 @@ #pragma once +#include "gdk/gdkdmabufformats.h" #include "gdksubsurfaceprivate.h" +#include "gdkdmabuf-wayland-private.h" #include "wayland-client-protocol.h" @@ -35,6 +37,8 @@ struct _GdkWaylandSubsurface struct wp_viewport *bg_viewport; cairo_rectangle_int_t bg_rect; gboolean bg_attached; + + DmabufFormatsInfo *formats; }; struct _GdkWaylandSubsurfaceClass @@ -48,4 +52,3 @@ void gdk_wayland_subsurface_request_frame (GdkSubsurface *subsurface); void gdk_wayland_subsurface_clear_frame_callback (GdkSubsurface *subsurface); GdkSubsurface * gdk_wayland_surface_create_subsurface (GdkSurface *surface); - diff --git a/gdk/wayland/gdksubsurface-wayland.c b/gdk/wayland/gdksubsurface-wayland.c index 666c66b428ae7a2c02543e2bd3b13967c6e6411a..4ae9fd50f60bbd545c2e065942b79a5b6b9aabae 100644 --- a/gdk/wayland/gdksubsurface-wayland.c +++ b/gdk/wayland/gdksubsurface-wayland.c @@ -27,6 +27,7 @@ #include "gdksubsurfaceprivate.h" #include "gdkdebugprivate.h" #include "gsk/gskrectprivate.h" +#include "gdkprivate.h" #include "linux-dmabuf-unstable-v1-client-protocol.h" @@ -51,6 +52,7 @@ gdk_wayland_subsurface_finalize (GObject *object) 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_clear_pointer (&self->formats, dmabuf_formats_info_free); G_OBJECT_CLASS (gdk_wayland_subsurface_parent_class)->finalize (object); } @@ -790,6 +792,15 @@ gdk_wayland_subsurface_clear_frame_callback (GdkSubsurface *sub) g_clear_pointer (&self->frame_callback, wl_callback_destroy); } +static void +dmabuf_formats_callback (gpointer data, + DmabufFormatsInfo *info) +{ + GdkSubsurface *sub = data; + + gdk_subsurface_set_dmabuf_formats (sub, info->formats); +} + GdkSubsurface * gdk_wayland_surface_create_subsurface (GdkSurface *surface) { @@ -798,6 +809,8 @@ gdk_wayland_surface_create_subsurface (GdkSurface *surface) GdkWaylandDisplay *disp = GDK_WAYLAND_DISPLAY (display); GdkWaylandSubsurface *sub; struct wl_region *region; + struct zwp_linux_dmabuf_feedback_v1 *feedback; + char *name; if (disp->subcompositor == NULL || disp->viewporter == NULL) { @@ -825,8 +838,28 @@ gdk_wayland_surface_create_subsurface (GdkSurface *surface) wl_region_add (sub->opaque_region, 0, 0, G_MAXINT, G_MAXINT); wl_surface_set_opaque_region (sub->surface, sub->opaque_region); + gdk_display_init_dmabuf (display); + + if (disp->linux_dmabuf) + { + dmabuf_formats_info_set_egl_formats (disp->dmabuf_formats_info, display->dmabuf_formats); + feedback = zwp_linux_dmabuf_v1_get_surface_feedback (disp->linux_dmabuf, sub->surface); + } + else + { + feedback = NULL; + } + + name = g_strdup_printf ("subsurface %p", sub); + sub->formats = dmabuf_formats_info_new (display, + name, + display->dmabuf_formats, + feedback, + dmabuf_formats_callback, + sub); + g_free (name); + GDK_DISPLAY_DEBUG (display, OFFLOAD, "Subsurface %p of surface %p created", sub, impl); return GDK_SUBSURFACE (sub); } - diff --git a/gdk/wayland/gdksurface-wayland-private.h b/gdk/wayland/gdksurface-wayland-private.h index b62e30f661e7927a2ac01d58576dceaf151a3a42..0021051458ad8661a34be596730863a1edeac622 100644 --- a/gdk/wayland/gdksurface-wayland-private.h +++ b/gdk/wayland/gdksurface-wayland-private.h @@ -89,6 +89,8 @@ struct _GdkWaylandSurface uint32_t last_configure_serial; int state_freeze_count; + + DmabufFormatsInfo *formats; }; typedef struct _GdkWaylandSurfaceClass GdkWaylandSurfaceClass; diff --git a/gdk/wayland/gdksurface-wayland.c b/gdk/wayland/gdksurface-wayland.c index 2ebeed05da08b8bb7661012de3ee96f937c82962..21ab187f5852018b6bafc3c256519f79a0412358 100644 --- a/gdk/wayland/gdksurface-wayland.c +++ b/gdk/wayland/gdksurface-wayland.c @@ -936,6 +936,15 @@ gdk_wayland_surface_create_wl_surface (GdkSurface *surface) self->display_server.wl_surface = wl_surface; } +static void +dmabuf_formats_callback (gpointer data, + DmabufFormatsInfo *info) +{ + GdkSurface *surface = data; + + gdk_surface_set_dmabuf_formats (surface, info->formats); +} + static void gdk_wayland_surface_constructed (GObject *object) { @@ -944,6 +953,8 @@ gdk_wayland_surface_constructed (GObject *object) GdkDisplay *display = gdk_surface_get_display (surface); GdkWaylandDisplay *display_wayland = GDK_WAYLAND_DISPLAY (display); GdkFrameClock *frame_clock = gdk_surface_get_frame_clock (surface); + struct zwp_linux_dmabuf_feedback_v1 *feedback; + char *name; self->event_queue = wl_display_create_queue (display_wayland->wl_display); display_wayland->event_queues = g_list_prepend (display_wayland->event_queues, @@ -969,6 +980,29 @@ gdk_wayland_surface_constructed (GObject *object) gdk_wayland_surface_create_wl_surface (surface); + gdk_display_init_dmabuf (display); + + if (display_wayland->linux_dmabuf) + { + dmabuf_formats_info_set_egl_formats (display_wayland->dmabuf_formats_info, + display->dmabuf_formats); + feedback = zwp_linux_dmabuf_v1_get_surface_feedback (display_wayland->linux_dmabuf, + self->display_server.wl_surface); + } + else + { + feedback = NULL; + } + + name = g_strdup_printf ("surface %p", surface); + self->formats = dmabuf_formats_info_new (display, + name, + display->dmabuf_formats, + feedback, + dmabuf_formats_callback, + surface); + g_free (name); + g_signal_connect (frame_clock, "before-paint", G_CALLBACK (on_frame_clock_before_paint), surface); g_signal_connect (frame_clock, "after-paint", G_CALLBACK (on_frame_clock_after_paint), surface); diff --git a/gdk/wayland/gdkwayland.h b/gdk/wayland/gdkwayland.h index 846445910e41e05b6d03e994cf2be5f1c8bcf52a..f366783f6185931ddd2a8f123905b42ee028af34 100644 --- a/gdk/wayland/gdkwayland.h +++ b/gdk/wayland/gdkwayland.h @@ -30,6 +30,7 @@ #include #include +#include #include #include #include diff --git a/gdk/wayland/gdkwaylanddmabufformats.h b/gdk/wayland/gdkwaylanddmabufformats.h new file mode 100644 index 0000000000000000000000000000000000000000..624acedbb0525829dd663f6b1fc481a45f82235d --- /dev/null +++ b/gdk/wayland/gdkwaylanddmabufformats.h @@ -0,0 +1,40 @@ +/* gdkwaylanddmabufformats.h + * + * Copyright 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 . + */ + +#pragma once + +#if !defined (__GDKWAYLAND_H_INSIDE__) && !defined (GTK_COMPILATION) +#error "Only can be included directly." +#endif + +#include + +G_BEGIN_DECLS + +GDK_AVAILABLE_IN_4_16 +dev_t gdk_wayland_dmabuf_formats_get_main_device (GdkDmabufFormats *formats); + +GDK_AVAILABLE_IN_4_16 +dev_t gdk_wayland_dmabuf_formats_get_target_device (GdkDmabufFormats *formats, + gsize idx); + +GDK_AVAILABLE_IN_4_16 +gboolean gdk_wayland_dmabuf_formats_is_scanout (GdkDmabufFormats *formats, + gsize idx); + +G_END_DECLS diff --git a/gdk/wayland/meson.build b/gdk/wayland/meson.build index fab877f3a85077df6a0ba136fcfe93e8a4a2457d..7a60950c213b04883da51e1d8ddb6be8cc67fe0e 100644 --- a/gdk/wayland/meson.build +++ b/gdk/wayland/meson.build @@ -30,6 +30,7 @@ gdk_wayland_sources = files([ gdk_wayland_public_headers = files([ 'gdkwaylanddevice.h', 'gdkwaylanddisplay.h', + 'gdkwaylanddmabufformats.h', 'gdkwaylandglcontext.h', 'gdkwaylandmonitor.h', 'gdkwaylandpopup.h', diff --git a/gsk/gskrenderer.c b/gsk/gskrenderer.c index b10286c0753a9ee803b9831b52e4dc2d6dea5c96..00b8e591a224d7176647672d65b97660a5f734ef 100644 --- a/gsk/gskrenderer.c +++ b/gsk/gskrenderer.c @@ -483,6 +483,12 @@ gsk_renderer_render (GskRenderer *renderer, if (priv->surface == NULL) return; + /* Offloading can change subsurface stacking, which may trigger + * surface property changes. We don't want them to take effect + * during frame processing. + */ + g_object_freeze_notify (G_OBJECT (priv->surface)); + renderer_class = GSK_RENDERER_GET_CLASS (renderer); clip = cairo_region_copy (region); @@ -528,6 +534,8 @@ gsk_renderer_render (GskRenderer *renderer, cairo_region_destroy (clip); g_clear_pointer (&offload, gsk_offload_free); priv->prev_node = gsk_render_node_ref (root); + + g_object_thaw_notify (G_OBJECT (priv->surface)); } /*< private > diff --git a/modules/media/gtkgstpaintable.c b/modules/media/gtkgstpaintable.c index 66bb29985d55c39ea9a076b7937d16bc382203ca..88dffbc06682e9a2e18313722b17a12ed2d66517 100644 --- a/modules/media/gtkgstpaintable.c +++ b/modules/media/gtkgstpaintable.c @@ -132,12 +132,15 @@ gtk_gst_paintable_video_renderer_create_video_sink (GstPlayerVideoRenderer *rend GstPlayer *player) { GtkGstPaintable *self = GTK_GST_PAINTABLE (renderer); + GdkDmabufFormats *dmabuf_formats; GstElement *sink; GdkGLContext *ctx; + dmabuf_formats = gdk_display_get_dmabuf_formats (gdk_display_get_default ()); sink = g_object_new (GTK_TYPE_GST_SINK, "paintable", self, "gl-context", self->context, + "dmabuf-formats", dmabuf_formats, NULL); if (self->context != NULL) diff --git a/modules/media/gtkgstsink.c b/modules/media/gtkgstsink.c index b870d6de3a655125ec5e95f2af23c2c952ec6dcb..c71335bc76e6e3db2de1385d9005281701572bf2 100644 --- a/modules/media/gtkgstsink.c +++ b/modules/media/gtkgstsink.c @@ -178,13 +178,12 @@ gtk_gst_sink_get_caps (GstBaseSink *bsink, { tmp = gst_pad_get_pad_template_caps (GST_BASE_SINK_PAD (bsink)); #ifdef HAVE_GSTREAMER_DRM - { - GdkDisplay *display = gdk_gl_context_get_display (self->gdk_context); - GdkDmabufFormats *formats = gdk_display_get_dmabuf_formats (display); + GdkSurface *surface; - tmp = gst_caps_make_writable (tmp); - add_drm_formats_and_modifiers (tmp, formats); - } + tmp = gst_caps_make_writable (tmp); + + surface = gdk_gl_context_get_surface (self->gdk_context); + add_drm_formats_and_modifiers (tmp, gdk_surface_get_dmabuf_formats (surface)); #endif } else diff --git a/testsuite/gdk/dmabufformats.c b/testsuite/gdk/dmabufformats.c index e2d69bdd8651276e6eb5b367619c6fbfe637727c..7cfeff057f108ae2e564203000a60a6dc7c0b168 100644 --- a/testsuite/gdk/dmabufformats.c +++ b/testsuite/gdk/dmabufformats.c @@ -5,6 +5,12 @@ #include #include #include +#include +#include + +#ifdef GDK_WINDOWING_WAYLAND +#include +#endif static void test_dmabuf_formats_basic (void) @@ -82,6 +88,102 @@ test_dmabuf_formats_builder (void) gdk_dmabuf_formats_unref (formats1); } +#define AAAA fourcc_code ('A', 'A', 'A', 'A') +#define BBBB fourcc_code ('B', 'B', 'B', 'B') +#define CCCC fourcc_code ('C', 'C', 'C', 'C') +#define DDDD fourcc_code ('D', 'D', 'D', 'D') + +static gboolean +dmabuf_format_matches (const GdkDmabufFormat *f1, guint32 fourcc, guint64 modifier, gsize next_priority) +{ + return f1->fourcc == fourcc && + f1->modifier == modifier && + f1->next_priority == next_priority; +} + +/* Test that sorting respects priorities, and the highest priority instance + * of duplicates is kept. + */ +static void +test_priorities (void) +{ + GdkDmabufFormatsBuilder *builder; + GdkDmabufFormats *formats; + const GdkDmabufFormat *f; + + builder = gdk_dmabuf_formats_builder_new (); + + gdk_dmabuf_formats_builder_add_format (builder, AAAA, DRM_FORMAT_MOD_LINEAR); + gdk_dmabuf_formats_builder_add_format (builder, BBBB, DRM_FORMAT_MOD_LINEAR); + gdk_dmabuf_formats_builder_add_format (builder, AAAA, I915_FORMAT_MOD_X_TILED); + gdk_dmabuf_formats_builder_next_priority (builder); + gdk_dmabuf_formats_builder_add_format (builder, DDDD, I915_FORMAT_MOD_X_TILED); + gdk_dmabuf_formats_builder_add_format (builder, BBBB, I915_FORMAT_MOD_X_TILED); + gdk_dmabuf_formats_builder_add_format (builder, CCCC, DRM_FORMAT_MOD_LINEAR); + gdk_dmabuf_formats_builder_add_format (builder, BBBB, DRM_FORMAT_MOD_LINEAR); // a duplicate + + formats = gdk_dmabuf_formats_builder_free_to_formats (builder); + + g_assert_true (gdk_dmabuf_formats_get_n_formats (formats) == 6); + + f = gdk_dmabuf_formats_peek_formats (formats); + + g_assert_true (dmabuf_format_matches (&f[0], AAAA, DRM_FORMAT_MOD_LINEAR, 3)); + g_assert_true (dmabuf_format_matches (&f[1], AAAA, I915_FORMAT_MOD_X_TILED, 3)); + g_assert_true (dmabuf_format_matches (&f[2], BBBB, DRM_FORMAT_MOD_LINEAR, 3)); + g_assert_true (dmabuf_format_matches (&f[3], BBBB, I915_FORMAT_MOD_X_TILED, 6)); + g_assert_true (dmabuf_format_matches (&f[4], CCCC, DRM_FORMAT_MOD_LINEAR, 6)); + g_assert_true (dmabuf_format_matches (&f[5], DDDD, I915_FORMAT_MOD_X_TILED, 6)); + + gdk_dmabuf_formats_unref (formats); +} + +static void +test_wayland (void) +{ + GdkDmabufFormatsBuilder *builder; + GdkDmabufFormats *formats1, *formats2; + + builder = gdk_dmabuf_formats_builder_new (); + gdk_dmabuf_formats_builder_add_format_for_device (builder, + DRM_FORMAT_RGBA8888, + 0, + DRM_FORMAT_MOD_LINEAR, + 0); + gdk_dmabuf_formats_builder_add_format_for_device (builder, + DRM_FORMAT_ARGB8888, + 0, + DRM_FORMAT_MOD_LINEAR, + 1); + formats1 = gdk_dmabuf_formats_builder_free_to_formats_for_device (builder, 2); + +#ifdef GDK_WINDOWING_WAYLAND + g_assert_true (gdk_wayland_dmabuf_formats_get_main_device (formats1) == 2); + g_assert_true (gdk_wayland_dmabuf_formats_get_target_device (formats1, 0) == 0); + g_assert_true (gdk_wayland_dmabuf_formats_get_target_device (formats1, 1) == 1); + g_assert_false (gdk_wayland_dmabuf_formats_is_scanout (formats1, 0)); + g_assert_false (gdk_wayland_dmabuf_formats_is_scanout (formats1, 1)); +#endif + + builder = gdk_dmabuf_formats_builder_new (); + gdk_dmabuf_formats_builder_add_format (builder, DRM_FORMAT_ARGB8888, DRM_FORMAT_MOD_LINEAR); + gdk_dmabuf_formats_builder_add_format (builder, DRM_FORMAT_RGBA8888, DRM_FORMAT_MOD_LINEAR); + formats2 = gdk_dmabuf_formats_builder_free_to_formats (builder); + +#ifdef GDK_WINDOWING_WAYLAND + g_assert_true (gdk_wayland_dmabuf_formats_get_main_device (formats2) == 0); + g_assert_true (gdk_wayland_dmabuf_formats_get_target_device (formats2, 0) == 0); + g_assert_true (gdk_wayland_dmabuf_formats_get_target_device (formats2, 1) == 0); + g_assert_false (gdk_wayland_dmabuf_formats_is_scanout (formats2, 0)); + g_assert_false (gdk_wayland_dmabuf_formats_is_scanout (formats2, 1)); +#endif + + g_assert_false (gdk_dmabuf_formats_equal (formats1, formats2)); + + gdk_dmabuf_formats_unref (formats1); + gdk_dmabuf_formats_unref (formats2); +} + int main (int argc, char *argv[]) { @@ -89,6 +191,8 @@ main (int argc, char *argv[]) g_test_add_func ("/dmabuf/formats/basic", test_dmabuf_formats_basic); g_test_add_func ("/dmabuf/formats/builder", test_dmabuf_formats_builder); + g_test_add_func ("/dmabuf/formats/priorities", test_priorities); + g_test_add_func ("/dmabuf/formats/wayland", test_wayland); return g_test_run (); }