[mutter] wayland/dma-buf: Add basic support for DMA buffer feedback



commit 7acecb1c7233dad0b9b314f59121882a518901d7
Author: Jonas Ã…dahl <jadahl gmail com>
Date:   Thu Aug 5 15:10:47 2021 +0200

    wayland/dma-buf: Add basic support for DMA buffer feedback
    
    This includes sending the default tranche, but so far only sends the
    same for every surface feedback requested. Scanout tranche will be added
    later.
    
    Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1959>

 meson.build                         |   2 +-
 src/wayland/meta-wayland-dma-buf.c  | 368 +++++++++++++++++++++++++++++++++++-
 src/wayland/meta-wayland-versions.h |   1 -
 3 files changed, 360 insertions(+), 11 deletions(-)
---
diff --git a/meson.build b/meson.build
index 96470d455f..d110d55cd8 100644
--- a/meson.build
+++ b/meson.build
@@ -42,7 +42,7 @@ gudev_req = '>= 232'
 
 # wayland version requirements
 wayland_server_req = '>= 1.18'
-wayland_protocols_req = '>= 1.23'
+wayland_protocols_req = '>= 1.24'
 
 # native backend version requirements
 libinput_req = '>= 1.19.0'
diff --git a/src/wayland/meta-wayland-dma-buf.c b/src/wayland/meta-wayland-dma-buf.c
index c5ca4ef659..c41577f9d2 100644
--- a/src/wayland/meta-wayland-dma-buf.c
+++ b/src/wayland/meta-wayland-dma-buf.c
@@ -40,6 +40,9 @@
 #include "wayland/meta-wayland-dma-buf.h"
 
 #include <drm_fourcc.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
 
 #include "backends/meta-backend-private.h"
 #include "backends/meta-egl-ext.h"
@@ -66,17 +69,41 @@
 
 #define META_WAYLAND_DMA_BUF_MAX_FDS 4
 
+/* Compatible with zwp_linux_dmabuf_feedback_v1.tranche_flags */
+typedef enum _MetaWaylandDmaBufTrancheFlags
+{
+  META_WAYLAND_DMA_BUF_TRANCHE_FLAG_NONE = 0,
+  META_WAYLAND_DMA_BUF_TRANCHE_FLAG_SCANOUT = 1,
+} MetaWaylandDmaBufTrancheFlags;
+
 typedef struct _MetaWaylandDmaBufFormat
 {
   uint32_t drm_format;
   uint64_t drm_modifier;
+  uint16_t table_index;
 } MetaWaylandDmaBufFormat;
 
+typedef struct _MetaWaylandDmaBufTranche
+{
+  dev_t target_device_id;
+  GArray *formats;
+  MetaWaylandDmaBufTrancheFlags flags;
+} MetaWaylandDmaBufTranche;
+
+typedef struct _MetaWaylandDmaBufFeedback
+{
+  dev_t main_device_id;
+  GList *tranches;
+} MetaWaylandDmaBufFeedback;
+
 struct _MetaWaylandDmaBufManager
 {
   MetaWaylandCompositor *compositor;
+  dev_t main_device_id;
 
   GArray *formats;
+  MetaAnonymousFile *format_table_file;
+  MetaWaylandDmaBufFeedback *default_feedback;
 };
 
 struct _MetaWaylandDmaBufBuffer
@@ -95,6 +122,118 @@ struct _MetaWaylandDmaBufBuffer
 
 G_DEFINE_TYPE (MetaWaylandDmaBufBuffer, meta_wayland_dma_buf_buffer, G_TYPE_OBJECT);
 
+static MetaWaylandDmaBufTranche *
+meta_wayland_dma_buf_tranche_new (dev_t                          device_id,
+                                  GArray                        *formats,
+                                  MetaWaylandDmaBufTrancheFlags  flags)
+{
+  MetaWaylandDmaBufTranche *tranche;
+
+  tranche = g_new0 (MetaWaylandDmaBufTranche, 1);
+  tranche->target_device_id = device_id;
+  tranche->formats = g_array_copy (formats);
+  tranche->flags = flags;
+
+  return tranche;
+}
+
+static void
+meta_wayland_dma_buf_tranche_free (MetaWaylandDmaBufTranche *tranche)
+{
+  g_clear_pointer (&tranche->formats, g_array_unref);
+  g_free (tranche);
+}
+
+static void
+meta_wayland_dma_buf_tranche_send (MetaWaylandDmaBufTranche *tranche,
+                                   struct wl_resource       *resource)
+{
+  struct wl_array target_device_buf;
+  dev_t *device_id_ptr;
+  struct wl_array formats_array;
+  unsigned int i;
+
+  wl_array_init (&target_device_buf);
+  device_id_ptr = wl_array_add (&target_device_buf, sizeof (*device_id_ptr));
+  *device_id_ptr = tranche->target_device_id;
+  zwp_linux_dmabuf_feedback_v1_send_tranche_target_device (resource,
+                                                           &target_device_buf);
+  wl_array_release (&target_device_buf);
+  zwp_linux_dmabuf_feedback_v1_send_tranche_flags (resource, tranche->flags);
+
+  wl_array_init (&formats_array);
+  for (i = 0; i < tranche->formats->len; i++)
+    {
+      MetaWaylandDmaBufFormat *format =
+        &g_array_index (tranche->formats,
+                        MetaWaylandDmaBufFormat,
+                        i);
+      uint16_t *format_index_ptr;
+
+      format_index_ptr = wl_array_add (&formats_array,
+                                       sizeof (*format_index_ptr));
+      *format_index_ptr = format->table_index;
+    }
+  zwp_linux_dmabuf_feedback_v1_send_tranche_formats (resource, &formats_array);
+  wl_array_release (&formats_array);
+
+  zwp_linux_dmabuf_feedback_v1_send_tranche_done (resource);
+}
+
+static void
+meta_wayland_dma_buf_feedback_send (MetaWaylandDmaBufFeedback *feedback,
+                                    MetaWaylandDmaBufManager  *dma_buf_manager,
+                                    struct wl_resource        *resource)
+{
+  size_t size;
+  int fd;
+  struct wl_array main_device_buf;
+  dev_t *device_id_ptr;
+
+  fd = meta_anonymous_file_open_fd (dma_buf_manager->format_table_file,
+                                    META_ANONYMOUS_FILE_MAPMODE_PRIVATE);
+  size = meta_anonymous_file_size (dma_buf_manager->format_table_file);
+  zwp_linux_dmabuf_feedback_v1_send_format_table (resource, fd, size);
+  meta_anonymous_file_close_fd (fd);
+
+  wl_array_init (&main_device_buf);
+  device_id_ptr = wl_array_add (&main_device_buf, sizeof (*device_id_ptr));
+  *device_id_ptr = feedback->main_device_id;
+  zwp_linux_dmabuf_feedback_v1_send_main_device (resource, &main_device_buf);
+
+  g_list_foreach (feedback->tranches,
+                  (GFunc) meta_wayland_dma_buf_tranche_send,
+                  resource);
+
+  zwp_linux_dmabuf_feedback_v1_send_done (resource);
+}
+
+static void
+meta_wayland_dma_buf_feedback_add_tranche (MetaWaylandDmaBufFeedback *feedback,
+                                           MetaWaylandDmaBufTranche  *tranche)
+{
+  feedback->tranches = g_list_append (feedback->tranches, tranche);
+}
+
+static MetaWaylandDmaBufFeedback *
+meta_wayland_dma_buf_feedback_new (dev_t device_id)
+{
+  MetaWaylandDmaBufFeedback *feedback;
+
+  feedback = g_new0 (MetaWaylandDmaBufFeedback, 1);
+  feedback->main_device_id = device_id;
+
+  return feedback;
+}
+
+static void
+meta_wayland_dma_buf_feedback_free (MetaWaylandDmaBufFeedback *feedback)
+{
+  g_clear_list (&feedback->tranches,
+                (GDestroyNotify) meta_wayland_dma_buf_tranche_free);
+  g_free (feedback);
+}
+
 static gboolean
 meta_wayland_dma_buf_realize_texture (MetaWaylandBuffer  *buffer,
                                       GError            **error)
@@ -630,10 +769,80 @@ dma_buf_handle_create_buffer_params (struct wl_client   *client,
                                   buffer_params_destructor);
 }
 
+static void
+feedback_destroy (struct wl_client   *client,
+                  struct wl_resource *resource)
+{
+  wl_resource_destroy (resource);
+}
+
+static const struct zwp_linux_dmabuf_feedback_v1_interface feedback_implementation =
+{
+  feedback_destroy,
+};
+
+static void
+feedback_destructor (struct wl_resource *resource)
+{
+}
+
+static void
+dma_buf_handle_get_default_feedback (struct wl_client   *client,
+                                     struct wl_resource *dma_buf_resource,
+                                     uint32_t            feedback_id)
+{
+  MetaWaylandDmaBufManager *dma_buf_manager =
+    wl_resource_get_user_data (dma_buf_resource);
+  struct wl_resource *feedback_resource;
+
+  feedback_resource =
+    wl_resource_create (client,
+                        &zwp_linux_dmabuf_feedback_v1_interface,
+                        wl_resource_get_version (dma_buf_resource),
+                        feedback_id);
+
+  wl_resource_set_implementation (feedback_resource,
+                                  &feedback_implementation,
+                                  NULL,
+                                  feedback_destructor);
+
+  meta_wayland_dma_buf_feedback_send (dma_buf_manager->default_feedback,
+                                      dma_buf_manager,
+                                      feedback_resource);
+}
+
+static void
+dma_buf_handle_get_surface_feedback (struct wl_client   *client,
+                                     struct wl_resource *dma_buf_resource,
+                                     uint32_t            feedback_id,
+                                     struct wl_resource *surface_resource)
+{
+  MetaWaylandDmaBufManager *dma_buf_manager =
+    wl_resource_get_user_data (dma_buf_resource);
+  struct wl_resource *feedback_resource;
+
+  feedback_resource =
+    wl_resource_create (client,
+                        &zwp_linux_dmabuf_feedback_v1_interface,
+                        wl_resource_get_version (dma_buf_resource),
+                        feedback_id);
+
+  wl_resource_set_implementation (feedback_resource,
+                                  &feedback_implementation,
+                                  NULL,
+                                  feedback_destructor);
+
+  meta_wayland_dma_buf_feedback_send (dma_buf_manager->default_feedback,
+                                      dma_buf_manager,
+                                      feedback_resource);
+}
+
 static const struct zwp_linux_dmabuf_v1_interface dma_buf_implementation =
 {
   dma_buf_handle_destroy,
   dma_buf_handle_create_buffer_params,
+  dma_buf_handle_get_default_feedback,
+  dma_buf_handle_get_surface_feedback,
 };
 
 static gboolean
@@ -661,6 +870,9 @@ static void
 send_modifiers (struct wl_resource      *resource,
                 MetaWaylandDmaBufFormat *format)
 {
+  g_assert (wl_resource_get_version (resource) <
+            ZWP_LINUX_DMABUF_V1_GET_DEFAULT_FEEDBACK_SINCE_VERSION);
+
   zwp_linux_dmabuf_v1_send_format (resource, format->drm_format);
 
   if (wl_resource_get_version (resource) <
@@ -681,22 +893,25 @@ dma_buf_bind (struct wl_client *client,
 {
   MetaWaylandDmaBufManager *dma_buf_manager = user_data;
   struct wl_resource *resource;
-  unsigned int i;
 
   resource = wl_resource_create (client, &zwp_linux_dmabuf_v1_interface,
                                  version, id);
   wl_resource_set_implementation (resource, &dma_buf_implementation,
                                   dma_buf_manager, NULL);
 
-
-  for (i = 0; i < dma_buf_manager->formats->len; i++)
+  if (version < ZWP_LINUX_DMABUF_V1_GET_DEFAULT_FEEDBACK_SINCE_VERSION)
     {
-      MetaWaylandDmaBufFormat *format =
-        &g_array_index (dma_buf_manager->formats,
-                        MetaWaylandDmaBufFormat,
-                        i);
+      unsigned int i;
+
+      for (i = 0; i < dma_buf_manager->formats->len; i++)
+        {
+          MetaWaylandDmaBufFormat *format =
+            &g_array_index (dma_buf_manager->formats,
+                            MetaWaylandDmaBufFormat,
+                            i);
 
-      send_modifiers (resource, format);
+          send_modifiers (resource, format);
+        }
     }
 }
 
@@ -741,6 +956,7 @@ add_format (MetaWaylandDmaBufManager *dma_buf_manager,
       format = (MetaWaylandDmaBufFormat) {
         .drm_format = drm_format,
         .drm_modifier = modifiers[i],
+        .table_index = dma_buf_manager->formats->len,
       };
       g_array_append_val (dma_buf_manager->formats, format);
     }
@@ -751,10 +967,53 @@ add_fallback:
   format = (MetaWaylandDmaBufFormat) {
     .drm_format = drm_format,
     .drm_modifier = DRM_FORMAT_MOD_INVALID,
+    .table_index = dma_buf_manager->formats->len,
   };
   g_array_append_val (dma_buf_manager->formats, format);
 }
 
+/*
+ * This is the structure the data is expected to have in the shared memory file
+ * shared with clients, according to the Wayland Linux DMA buffer protocol.
+ * It's structured as 16 bytes (128 bits) per entry, where each entry consists
+ * of the following:
+ *
+ * [ 32 bit format  ][ 32 bit padding ][          64 bit modifier         ]
+ */
+typedef struct _MetaMetaWaylanDmaBdufFormatEntry
+{
+  uint32_t drm_format;
+  uint32_t unused_padding;
+  uint64_t drm_modifier;
+} MetaWaylandDmaBufFormatEntry;
+
+G_STATIC_ASSERT (sizeof (MetaWaylandDmaBufFormatEntry) == 16);
+G_STATIC_ASSERT (offsetof (MetaWaylandDmaBufFormatEntry, drm_format) == 0);
+G_STATIC_ASSERT (offsetof (MetaWaylandDmaBufFormatEntry, drm_modifier) == 8);
+
+static void
+init_format_table (MetaWaylandDmaBufManager *dma_buf_manager)
+{
+  g_autofree MetaWaylandDmaBufFormatEntry *format_table = NULL;
+  size_t size;
+  int i;
+
+  size = sizeof (MetaWaylandDmaBufFormatEntry) * dma_buf_manager->formats->len;
+  format_table = g_malloc0 (size);
+
+  for (i = 0; i < dma_buf_manager->formats->len; i++)
+    {
+      MetaWaylandDmaBufFormat *format =
+        &g_array_index (dma_buf_manager->formats, MetaWaylandDmaBufFormat, i);
+
+      format_table[i].drm_format = format->drm_format;
+      format_table[i].drm_modifier = format->drm_modifier;
+    }
+
+  dma_buf_manager->format_table_file =
+    meta_anonymous_file_new (size, (uint8_t *) format_table);
+}
+
 static void
 init_formats (MetaWaylandDmaBufManager *dma_buf_manager,
               EGLDisplay                egl_display)
@@ -775,6 +1034,25 @@ init_formats (MetaWaylandDmaBufManager *dma_buf_manager,
   add_format (dma_buf_manager, egl_display, DRM_FORMAT_XBGR16161616F);
   add_format (dma_buf_manager, egl_display, DRM_FORMAT_XRGB16161616F);
   add_format (dma_buf_manager, egl_display, DRM_FORMAT_ARGB16161616F);
+
+  init_format_table (dma_buf_manager);
+}
+
+static void
+init_default_feedback (MetaWaylandDmaBufManager *dma_buf_manager)
+{
+  MetaWaylandDmaBufTrancheFlags flags;
+  MetaWaylandDmaBufTranche *tranche;
+
+  dma_buf_manager->default_feedback =
+    meta_wayland_dma_buf_feedback_new (dma_buf_manager->main_device_id);
+
+  flags = META_WAYLAND_DMA_BUF_TRANCHE_FLAG_NONE;
+  tranche = meta_wayland_dma_buf_tranche_new (dma_buf_manager->main_device_id,
+                                              dma_buf_manager->formats,
+                                              flags);
+  meta_wayland_dma_buf_feedback_add_tranche (dma_buf_manager->default_feedback,
+                                             tranche);
 }
 
 /**
@@ -794,8 +1072,14 @@ meta_wayland_dma_buf_manager_new (MetaWaylandCompositor  *compositor,
   ClutterBackend *clutter_backend = meta_backend_get_clutter_backend (backend);
   CoglContext *cogl_context = clutter_backend_get_cogl_context (clutter_backend);
   EGLDisplay egl_display = cogl_egl_context_get_egl_display (cogl_context);
+  dev_t device_id = 0;
+  int protocol_version;
+  EGLDeviceEXT egl_device;
+  EGLAttrib attrib;
   g_autoptr (GError) local_error = NULL;
   g_autofree MetaWaylandDmaBufManager *dma_buf_manager = NULL;
+  const char *device_path = NULL;
+  struct stat device_stat;
 
   g_assert (backend && egl && clutter_backend && cogl_context && egl_display);
 
@@ -808,11 +1092,71 @@ meta_wayland_dma_buf_manager_new (MetaWaylandCompositor  *compositor,
       return NULL;
     }
 
+  if (!meta_egl_query_display_attrib (egl, egl_display,
+                                      EGL_DEVICE_EXT, &attrib,
+                                      &local_error))
+    {
+      g_warning ("Failed to query EGL device from primary EGL display: %s",
+                 local_error->message);
+      protocol_version = 3;
+      goto initialize;
+    }
+  egl_device = (EGLDeviceEXT) attrib;
+
+  if (meta_egl_egl_device_has_extensions (egl, egl_device, NULL,
+                                          "EGL_EXT_device_drm_render_node",
+                                          NULL))
+    {
+      device_path = meta_egl_query_device_string (egl, egl_device,
+                                                  EGL_DRM_RENDER_NODE_FILE_EXT,
+                                                  &local_error);
+    }
+  else if (meta_egl_egl_device_has_extensions (egl, egl_device, NULL,
+                                               "EGL_EXT_device_drm",
+                                               NULL))
+    {
+      device_path = meta_egl_query_device_string (egl, egl_device,
+                                                  EGL_DRM_DEVICE_FILE_EXT,
+                                                  &local_error);
+    }
+  else
+    {
+      meta_topic (META_DEBUG_WAYLAND,
+                  "Only advertising zwp_linux_dmabuf_v1 interface version 3 "
+                  "support, missing 'EGL_EXT_device_drm' and "
+                  "'EGL_EXT_device_drm_render_node'");
+      protocol_version = 3;
+      goto initialize;
+    }
+
+  if (!device_path)
+    {
+      g_warning ("Failed to query EGL device path: %s",
+                 local_error->message);
+      protocol_version = 3;
+      goto initialize;
+    }
+
+  if (stat (device_path, &device_stat) != 0)
+    {
+      g_warning ("Failed to fetch device file ID for '%s': %s",
+                 device_path,
+                 g_strerror (errno));
+      protocol_version = 3;
+      goto initialize;
+    }
+
+  device_id = device_stat.st_rdev;
+
+  protocol_version = 4;
+
+initialize:
+
   dma_buf_manager = g_new0 (MetaWaylandDmaBufManager, 1);
 
   if (!wl_global_create (compositor->wayland_display,
                          &zwp_linux_dmabuf_v1_interface,
-                         META_ZWP_LINUX_DMABUF_V1_VERSION,
+                         protocol_version,
                          dma_buf_manager,
                          dma_buf_bind))
     {
@@ -822,8 +1166,10 @@ meta_wayland_dma_buf_manager_new (MetaWaylandCompositor  *compositor,
     }
 
   dma_buf_manager->compositor = compositor;
+  dma_buf_manager->main_device_id = device_id;
 
   init_formats (dma_buf_manager, egl_display);
+  init_default_feedback (dma_buf_manager);
 
   return g_steal_pointer (&dma_buf_manager);
 }
@@ -831,7 +1177,11 @@ meta_wayland_dma_buf_manager_new (MetaWaylandCompositor  *compositor,
 void
 meta_wayland_dma_buf_manager_free (MetaWaylandDmaBufManager *dma_buf_manager)
 {
+  g_clear_pointer (&dma_buf_manager->format_table_file,
+                   meta_anonymous_file_free);
   g_clear_pointer (&dma_buf_manager->formats, g_array_unref);
+  g_clear_pointer (&dma_buf_manager->default_feedback,
+                   meta_wayland_dma_buf_feedback_free);
   g_free (dma_buf_manager);
 }
 
diff --git a/src/wayland/meta-wayland-versions.h b/src/wayland/meta-wayland-versions.h
index 5e40b4aa22..291a75d3dd 100644
--- a/src/wayland/meta-wayland-versions.h
+++ b/src/wayland/meta-wayland-versions.h
@@ -48,7 +48,6 @@
 #define META_ZWP_POINTER_GESTURES_V1_VERSION 3
 #define META_ZXDG_EXPORTER_V1_VERSION       1
 #define META_ZXDG_IMPORTER_V1_VERSION       1
-#define META_ZWP_LINUX_DMABUF_V1_VERSION    3
 #define META_ZWP_KEYBOARD_SHORTCUTS_INHIBIT_V1_VERSION 1
 #define META_ZXDG_OUTPUT_V1_VERSION         3
 #define META_ZWP_XWAYLAND_KEYBOARD_GRAB_V1_VERSION 1


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]