From e8b890ab53a67da2dcb9a9993f143a58c403daa1 Mon Sep 17 00:00:00 2001 From: Austin Shafer Date: Fri, 8 Sep 2023 16:40:56 -0400 Subject: [PATCH] wayland: Implement linux-drm-syncobj-v1 This implements the explicit sync protocol linux-drm-syncobj-v1. This works by importing a DRM syncobj timeline and importing/exporting fds to/from the sync points on the timeline corresponding to buffer acquire and release. We take fds for sync points provided during a surface commit and use them to delay transaction application, and fetch fds from Cogl to signal when we are done using a particular buffer. Part-of: --- src/meson.build | 2 + src/wayland/meta-wayland-buffer.c | 38 +- src/wayland/meta-wayland-buffer.h | 2 + src/wayland/meta-wayland-dma-buf.c | 34 + src/wayland/meta-wayland-dma-buf.h | 7 + src/wayland/meta-wayland-linux-drm-syncobj.c | 637 +++++++++++++++++++ src/wayland/meta-wayland-linux-drm-syncobj.h | 59 ++ src/wayland/meta-wayland-surface-private.h | 7 + src/wayland/meta-wayland-surface.c | 18 + src/wayland/meta-wayland-transaction.c | 54 +- src/wayland/meta-wayland-types.h | 2 + src/wayland/meta-wayland.c | 2 + 12 files changed, 853 insertions(+), 9 deletions(-) create mode 100644 src/wayland/meta-wayland-linux-drm-syncobj.c create mode 100644 src/wayland/meta-wayland-linux-drm-syncobj.h diff --git a/src/meson.build b/src/meson.build index c40f4ca64..90e2be0af 100644 --- a/src/meson.build +++ b/src/meson.build @@ -635,6 +635,8 @@ if have_wayland 'wayland/meta-wayland-keyboard.h', 'wayland/meta-wayland-legacy-xdg-foreign.c', 'wayland/meta-wayland-legacy-xdg-foreign.h', + 'wayland/meta-wayland-linux-drm-syncobj.c', + 'wayland/meta-wayland-linux-drm-syncobj.h', 'wayland/meta-wayland-outputs.c', 'wayland/meta-wayland-outputs.h', 'wayland/meta-wayland-pointer.c', diff --git a/src/wayland/meta-wayland-buffer.c b/src/wayland/meta-wayland-buffer.c index 2c64674e9..9d6faf479 100644 --- a/src/wayland/meta-wayland-buffer.c +++ b/src/wayland/meta-wayland-buffer.c @@ -49,6 +49,7 @@ #include "wayland/meta-wayland-buffer.h" #include +#include #include "backends/meta-backend-private.h" #include "clutter/clutter.h" @@ -58,6 +59,8 @@ #include "wayland/meta-wayland-private.h" #include "common/meta-cogl-drm-formats.h" #include "compositor/meta-multi-texture-format-private.h" +#include "wayland/meta-drm-timeline.h" +#include "wayland/meta-wayland-linux-drm-syncobj.h" #ifdef HAVE_NATIVE_BACKEND #include "backends/native/meta-drm-buffer-gbm.h" @@ -711,12 +714,43 @@ meta_wayland_buffer_inc_use_count (MetaWaylandBuffer *buffer) void meta_wayland_buffer_dec_use_count (MetaWaylandBuffer *buffer) { + MetaContext *context = meta_wayland_compositor_get_context (buffer->compositor); + MetaBackend *backend = meta_context_get_backend (context); + ClutterBackend *clutter_backend = meta_backend_get_clutter_backend (backend); + CoglContext *cogl_context = clutter_backend_get_cogl_context (clutter_backend); + MetaWaylandSyncPoint *sync_point; + g_autoptr(GError) error = NULL; + g_autofd int sync_fd = -1; + g_return_if_fail (buffer->use_count > 0); buffer->use_count--; if (buffer->use_count == 0 && buffer->resource) - wl_buffer_send_release (buffer->resource); + { + wl_buffer_send_release (buffer->resource); + + sync_fd = cogl_context_get_latest_sync_fd (cogl_context); + if (sync_fd < 0) + { + meta_topic (META_DEBUG_WAYLAND, "Invalid Sync Fd returned by COGL"); + return; + } + + for (int i = 0; i < buffer->release_points->len; i++) + { + sync_point = g_ptr_array_index (buffer->release_points, i); + if (!meta_wayland_sync_timeline_set_sync_point (sync_point->timeline, + sync_point->sync_point, + sync_fd, + &error)) + { + g_warning ("Failed to import sync point: %s", error->message); + } + } + g_ptr_array_remove_range (buffer->release_points, 0, + buffer->release_points->len); + } } gboolean @@ -980,6 +1014,7 @@ meta_wayland_buffer_finalize (GObject *object) clear_tainted_scanout_onscreens (buffer); g_clear_pointer (&buffer->tainted_scanout_onscreens, g_hash_table_unref); + g_clear_pointer (&buffer->release_points, g_ptr_array_unref); g_clear_object (&buffer->egl_image.texture); #ifdef HAVE_WAYLAND_EGLSTREAM @@ -998,6 +1033,7 @@ meta_wayland_buffer_finalize (GObject *object) static void meta_wayland_buffer_init (MetaWaylandBuffer *buffer) { + buffer->release_points = g_ptr_array_new_with_free_func (g_free); } static void diff --git a/src/wayland/meta-wayland-buffer.h b/src/wayland/meta-wayland-buffer.h index 23aea7cb2..a0d59f51b 100644 --- a/src/wayland/meta-wayland-buffer.h +++ b/src/wayland/meta-wayland-buffer.h @@ -79,6 +79,8 @@ struct _MetaWaylandBuffer } single_pixel; GHashTable *tainted_scanout_onscreens; + + GPtrArray *release_points; }; #define META_TYPE_WAYLAND_BUFFER (meta_wayland_buffer_get_type ()) diff --git a/src/wayland/meta-wayland-dma-buf.c b/src/wayland/meta-wayland-dma-buf.c index 221df3dc0..d23597b7f 100644 --- a/src/wayland/meta-wayland-dma-buf.c +++ b/src/wayland/meta-wayland-dma-buf.c @@ -56,6 +56,7 @@ #include "wayland/meta-wayland-buffer.h" #include "wayland/meta-wayland-private.h" #include "wayland/meta-wayland-versions.h" +#include "wayland/meta-wayland-linux-drm-syncobj.h" #ifdef HAVE_NATIVE_BACKEND #include "backends/native/meta-drm-buffer-gbm.h" @@ -1045,6 +1046,39 @@ meta_wayland_dma_buf_create_source (MetaWaylandBuffer *buffer, return &source->base; } +GSource * +meta_wayland_drm_syncobj_create_source (MetaWaylandBuffer *buffer, + MetaWaylandSyncobjTimeline *timeline, + uint64_t sync_point, + MetaWaylandDmaBufSourceDispatch dispatch, + gpointer user_data) +{ + MetaWaylandDmaBufSource *source = NULL; + g_autofd int sync_fd = -1; + g_autoptr(GError) error = NULL; + + sync_fd = meta_wayland_sync_timeline_get_eventfd (timeline, sync_point, &error); + if (sync_fd < 0) + { + g_warning ("Failed to get sync fd: %s", error->message); + return NULL; + } + + if (is_fd_readable (sync_fd)) + { + return NULL; + } + + source = create_source (buffer, dispatch, user_data); + if (!source) + return NULL; + + source->fd_tags[0] = g_source_add_unix_fd (&source->base, sync_fd, G_IO_IN); + source->owned_sync_fd[0] = g_steal_fd (&sync_fd); + + return &source->base; +} + static void buffer_params_create_common (struct wl_client *client, struct wl_resource *params_resource, diff --git a/src/wayland/meta-wayland-dma-buf.h b/src/wayland/meta-wayland-dma-buf.h index 8f71dac42..5b2cda734 100644 --- a/src/wayland/meta-wayland-dma-buf.h +++ b/src/wayland/meta-wayland-dma-buf.h @@ -63,6 +63,13 @@ meta_wayland_dma_buf_create_source (MetaWaylandBuffer *buffer, MetaWaylandDmaBufSourceDispatch dispatch, gpointer user_data); +GSource * +meta_wayland_drm_syncobj_create_source (MetaWaylandBuffer *buffer, + MetaWaylandSyncobjTimeline *timeline, + uint64_t sync_point, + MetaWaylandDmaBufSourceDispatch dispatch, + gpointer user_data); + CoglScanout * meta_wayland_dma_buf_try_acquire_scanout (MetaWaylandBuffer *buffer, CoglOnscreen *onscreen, diff --git a/src/wayland/meta-wayland-linux-drm-syncobj.c b/src/wayland/meta-wayland-linux-drm-syncobj.c new file mode 100644 index 000000000..a922415d9 --- /dev/null +++ b/src/wayland/meta-wayland-linux-drm-syncobj.c @@ -0,0 +1,637 @@ +/* + * Copyright (C) 2023 NVIDIA Corporation. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + * Written by: + * Austin Shafer + */ + +#include "config.h" + +#include "backends/native/meta-backend-native-types.h" +#include "backends/native/meta-device-pool.h" +#include "backends/native/meta-renderer-native.h" +#include "meta/util.h" +#include "wayland/meta-wayland-buffer.h" +#include "wayland/meta-wayland-linux-drm-syncobj.h" +#include "wayland/meta-wayland-private.h" +#include +#include + +typedef struct _MetaWaylandDrmSyncobjManager +{ + GObject parent; + + int drm; +} MetaWaylandDrmSyncobjManager; + +typedef struct _MetaWaylandSyncobjSurface +{ + GObject parent; + + struct wl_resource *resource; + MetaWaylandSurface *surface; + gulong surface_destroy_handler_id; +} MetaWaylandSyncobjSurface; + +typedef struct _MetaWaylandSyncobjTimeline +{ + GObject parent; + + MetaDrmTimeline *drm_timeline; +} MetaWaylandSyncobjTimeline; + +#define META_TYPE_WAYLAND_DRM_SYNCOBJ_MANAGER (meta_wayland_drm_syncobj_manager_get_type ()) +G_DECLARE_FINAL_TYPE (MetaWaylandDrmSyncobjManager, meta_wayland_drm_syncobj_manager, + META, WAYLAND_DRM_SYNCOBJ_MANAGER, GObject) + +#define META_TYPE_WAYLAND_SYNCOBJ_SURFACE (meta_wayland_syncobj_surface_get_type ()) +G_DECLARE_FINAL_TYPE (MetaWaylandSyncobjSurface, meta_wayland_syncobj_surface, + META, WAYLAND_SYNCOBJ_SURFACE, GObject) + +#define META_TYPE_WAYLAND_SYNCOBJ_TIMELINE (meta_wayland_syncobj_timeline_get_type ()) +G_DECLARE_FINAL_TYPE (MetaWaylandSyncobjTimeline, meta_wayland_syncobj_timeline, + META, WAYLAND_SYNCOBJ_TIMELINE, GObject) + +#define META_TYPE_WAYLAND_DRM_SYNCOBJ_MANAGER (meta_wayland_drm_syncobj_manager_get_type ()) +G_DEFINE_FINAL_TYPE (MetaWaylandDrmSyncobjManager, meta_wayland_drm_syncobj_manager, + G_TYPE_OBJECT) + +#define META_TYPE_WAYLAND_SYNCOBJ_SURFACE (meta_wayland_syncobj_surface_get_type ()) +G_DEFINE_FINAL_TYPE (MetaWaylandSyncobjSurface, meta_wayland_syncobj_surface, + G_TYPE_OBJECT) + +#define META_TYPE_WAYLAND_SYNCOBJ_TIMELINE (meta_wayland_syncobj_timeline_get_type ()) +G_DEFINE_FINAL_TYPE (MetaWaylandSyncobjTimeline, meta_wayland_syncobj_timeline, + G_TYPE_OBJECT) + +G_DEFINE_FINAL_TYPE (MetaWaylandSyncPoint, meta_wayland_sync_point, G_TYPE_OBJECT); + +static GQuark quark_syncobj_surface; + +static void +meta_wayland_sync_point_set (MetaWaylandSyncPoint **sync_point_ptr, + MetaWaylandSyncobjTimeline *syncobj_timeline, + uint32_t point_hi, + uint32_t point_lo) +{ + MetaWaylandSyncPoint *sync_point; + + if (!*sync_point_ptr) + *sync_point_ptr = g_object_new (META_TYPE_WAYLAND_SYNC_POINT, NULL); + + sync_point = *sync_point_ptr; + g_set_object (&sync_point->timeline, syncobj_timeline); + sync_point->sync_point = (uint64_t)point_hi << 32 | point_lo; +} + +static void +meta_wayland_sync_point_finalize (GObject *object) +{ + MetaWaylandSyncPoint *sync = META_WAYLAND_SYNC_POINT (object); + + g_object_unref (sync->timeline); + + G_OBJECT_CLASS (meta_wayland_sync_point_parent_class)->finalize (object); +} + +static void +meta_wayland_sync_point_init (MetaWaylandSyncPoint *sync) +{ +} + +static void +meta_wayland_sync_point_class_init (MetaWaylandSyncPointClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = meta_wayland_sync_point_finalize; +} + +static void +syncobj_timeline_handle_resource_destroy (struct wl_resource *resource) +{ + MetaWaylandSyncobjTimeline *syncobj_timeline = + wl_resource_get_user_data (resource); + g_object_unref (syncobj_timeline); +} + +static void +meta_wayland_syncobj_timeline_finalize (GObject *object) +{ + MetaWaylandSyncobjTimeline *syncobj_timeline = + META_WAYLAND_SYNCOBJ_TIMELINE (object); + + g_clear_object (&syncobj_timeline->drm_timeline); + + G_OBJECT_CLASS (meta_wayland_syncobj_timeline_parent_class)->finalize (object); +} + +static void +meta_wayland_syncobj_timeline_class_init (MetaWaylandSyncobjTimelineClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = meta_wayland_syncobj_timeline_finalize; +} + +static void +meta_wayland_syncobj_timeline_init (MetaWaylandSyncobjTimeline *syncobj_timeline) +{ + syncobj_timeline->drm_timeline = NULL; +} + +static void +syncobj_timeline_handle_destroy (struct wl_client *client, + struct wl_resource *resource) +{ + wl_resource_destroy (resource); +} + +static const struct wp_linux_drm_syncobj_timeline_v1_interface + syncobj_timeline_implementation = +{ + syncobj_timeline_handle_destroy, +}; + +gboolean +meta_wayland_sync_timeline_set_sync_point (MetaWaylandSyncobjTimeline *timeline, + uint64_t sync_point, + int sync_fd, + GError **error) +{ + return meta_drm_timeline_set_sync_point (timeline->drm_timeline, + sync_point, + sync_fd, + error); +} + +int +meta_wayland_sync_timeline_get_eventfd (MetaWaylandSyncobjTimeline *timeline, + uint64_t sync_point, + GError **error) +{ + return meta_drm_timeline_get_eventfd (timeline->drm_timeline, + sync_point, + error); +} + +static void +syncobj_surface_handle_destroy (struct wl_client *client, + struct wl_resource *resource) +{ + wl_resource_destroy (resource); +} + +static void +syncobj_surface_handle_set_acquire_point (struct wl_client *client, + struct wl_resource *resource, + struct wl_resource *timeline_resource, + uint32_t point_hi, + uint32_t point_lo) +{ + MetaWaylandSyncobjSurface *syncobj_surface = wl_resource_get_user_data (resource); + MetaWaylandSurface *surface = syncobj_surface->surface; + MetaWaylandSyncobjTimeline *syncobj_timeline = + wl_resource_get_user_data (timeline_resource); + + if (!surface) + { + wl_resource_post_error (resource, + WP_LINUX_DRM_SYNCOBJ_SURFACE_V1_ERROR_NO_SURFACE, + "Underlying surface object has been destroyed"); + return; + } + + meta_wayland_sync_point_set (&surface->pending_state->drm_syncobj.acquire, + syncobj_timeline, + point_hi, + point_lo); +} + +static void syncobj_surface_handle_set_release_point (struct wl_client *client, + struct wl_resource *resource, + struct wl_resource *timeline_resource, + uint32_t point_hi, + uint32_t point_lo) +{ + MetaWaylandSyncobjSurface *syncobj_surface = wl_resource_get_user_data (resource); + MetaWaylandSurface *surface = syncobj_surface->surface; + MetaWaylandSyncobjTimeline *syncobj_timeline = + wl_resource_get_user_data (timeline_resource); + + if (!surface) + { + wl_resource_post_error (resource, + WP_LINUX_DRM_SYNCOBJ_SURFACE_V1_ERROR_NO_SURFACE, + "Underlying surface object has been destroyed"); + return; + } + + meta_wayland_sync_point_set (&surface->pending_state->drm_syncobj.release, + syncobj_timeline, + point_hi, + point_lo); +} + +static const struct wp_linux_drm_syncobj_surface_v1_interface + syncobj_surface_implementation = +{ + syncobj_surface_handle_destroy, + syncobj_surface_handle_set_acquire_point, + syncobj_surface_handle_set_release_point, +}; + +static void +syncobj_surface_resource_destroyed (MetaWaylandSurface *surface, + MetaWaylandSyncobjSurface *syncobj_surface) +{ + g_clear_signal_handler (&syncobj_surface->surface_destroy_handler_id, + syncobj_surface->surface); + + g_object_set_qdata (G_OBJECT (syncobj_surface->surface), + quark_syncobj_surface, + NULL); + + syncobj_surface->surface = NULL; +} + +static void +syncobj_surface_destructor (struct wl_resource *resource) +{ + MetaWaylandSyncobjSurface *syncobj_surface = + wl_resource_get_user_data (resource); + + if (syncobj_surface->surface) + syncobj_surface_resource_destroyed (syncobj_surface->surface, syncobj_surface); + + g_object_unref (syncobj_surface); +} + +static void +meta_wayland_syncobj_surface_class_init (MetaWaylandSyncobjSurfaceClass *klass) +{ +} + +static void +meta_wayland_syncobj_surface_init (MetaWaylandSyncobjSurface *syncobj_surface) +{ +} + +static void +drm_syncobj_manager_handle_destroy (struct wl_client *client, + struct wl_resource *resource) +{ + wl_resource_destroy (resource); +} + +static void +drm_syncobj_manager_handle_get_surface (struct wl_client *client, + struct wl_resource *resource, + uint32_t id, + struct wl_resource *surface_resource) +{ + MetaWaylandSurface *surface = wl_resource_get_user_data (surface_resource); + MetaWaylandSyncobjSurface *syncobj_surface = + g_object_get_qdata (G_OBJECT (surface), quark_syncobj_surface); + struct wl_resource *sync_resource; + + if (syncobj_surface) + { + wl_resource_post_error (surface_resource, + WP_LINUX_DRM_SYNCOBJ_MANAGER_V1_ERROR_SURFACE_EXISTS, + "DRM Syncobj surface object already created for surface %d", + wl_resource_get_id (surface_resource)); + return; + } + + sync_resource = + wl_resource_create (client, + &wp_linux_drm_syncobj_surface_v1_interface, + wl_resource_get_version (resource), + id); + if (sync_resource == NULL) + { + wl_resource_post_no_memory (resource); + return; + } + + syncobj_surface = g_object_new (META_TYPE_WAYLAND_SYNCOBJ_SURFACE, NULL); + syncobj_surface->surface = surface; + syncobj_surface->surface_destroy_handler_id = + g_signal_connect (surface, + "destroy", + G_CALLBACK (syncobj_surface_resource_destroyed), + syncobj_surface); + + g_object_set_qdata (G_OBJECT (surface), + quark_syncobj_surface, + syncobj_surface); + + wl_resource_set_implementation (sync_resource, + &syncobj_surface_implementation, + syncobj_surface, + syncobj_surface_destructor); + syncobj_surface->resource = sync_resource; +} + +static void +drm_syncobj_manager_handle_import_timeline (struct wl_client *client, + struct wl_resource *resource, + uint32_t id, + int drm_syncobj_fd) +{ + MetaWaylandDrmSyncobjManager *drm_syncobj = wl_resource_get_user_data (resource); + g_autoptr (GError) error = NULL; + g_autoptr (MetaDrmTimeline) drm_timeline = NULL; + g_autoptr (MetaWaylandSyncobjTimeline) syncobj_timeline = NULL; + struct wl_resource *timeline_resource; + + drm_timeline = meta_drm_timeline_import_syncobj (drm_syncobj->drm, + drm_syncobj_fd, + &error); + close (drm_syncobj_fd); + if (!drm_timeline) + { + wl_resource_post_error (resource, + WP_LINUX_DRM_SYNCOBJ_MANAGER_V1_ERROR_INVALID_TIMELINE, + "Failed to import DRM syncobj: %s", + error->message); + return; + } + + syncobj_timeline = g_object_new (META_TYPE_WAYLAND_SYNCOBJ_TIMELINE, NULL); + + timeline_resource = wl_resource_create (client, + &wp_linux_drm_syncobj_timeline_v1_interface, + wl_resource_get_version (resource), + id); + if (timeline_resource == NULL) + { + wl_resource_post_no_memory (resource); + return; + } + + syncobj_timeline->drm_timeline = g_steal_pointer (&drm_timeline); + wl_resource_set_implementation (timeline_resource, + &syncobj_timeline_implementation, + g_steal_pointer (&syncobj_timeline), + syncobj_timeline_handle_resource_destroy); +} + +static const struct wp_linux_drm_syncobj_manager_v1_interface + drm_syncobj_manager_implementation = +{ + drm_syncobj_manager_handle_destroy, + drm_syncobj_manager_handle_get_surface, + drm_syncobj_manager_handle_import_timeline, +}; + +static void +meta_wayland_drm_syncobj_manager_finalize (GObject *object) +{ + MetaWaylandDrmSyncobjManager *drm_syncobj = + META_WAYLAND_DRM_SYNCOBJ_MANAGER (object); + + g_clear_fd (&drm_syncobj->drm, NULL); + + G_OBJECT_CLASS (meta_wayland_drm_syncobj_manager_parent_class)->finalize (object); +} + +static void +meta_wayland_drm_syncobj_manager_class_init (MetaWaylandDrmSyncobjManagerClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = meta_wayland_drm_syncobj_manager_finalize; + + quark_syncobj_surface = g_quark_from_static_string ("drm-syncobj-quark"); +} + +static void +meta_wayland_drm_syncobj_manager_init (MetaWaylandDrmSyncobjManager *drm_syncobj) +{ + drm_syncobj->drm = -1; +} + +static void +drm_syncobj_manager_bind (struct wl_client *client, + void *user_data, + uint32_t version, + uint32_t id) +{ + MetaWaylandDrmSyncobjManager *drm_syncobj_manager = user_data; + struct wl_resource *resource; + + resource = wl_resource_create (client, + &wp_linux_drm_syncobj_manager_v1_interface, + version, + id); + wl_resource_set_implementation (resource, + &drm_syncobj_manager_implementation, + drm_syncobj_manager, + NULL); +} + +static MetaWaylandDrmSyncobjManager * +meta_wayland_drm_syncobj_manager_new (MetaWaylandCompositor *compositor, + GError **error) +{ + MetaContext *context = + meta_wayland_compositor_get_context (compositor); + MetaBackend *backend = meta_context_get_backend (context); + MetaEgl *egl = meta_backend_get_egl (backend); + 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); + MetaWaylandDrmSyncobjManager *drm_syncobj_manager; + EGLDeviceEXT egl_device; + g_autofd int drm_fd = -1; + EGLAttrib attrib; + uint64_t timeline_supported = false; + const char *device_path = NULL; + + g_assert (backend && egl && clutter_backend && cogl_context && egl_display); + + if (!meta_egl_query_display_attrib (egl, egl_display, + EGL_DEVICE_EXT, &attrib, + error)) + return NULL; + + egl_device = (EGLDeviceEXT) attrib; + + if (meta_egl_egl_device_has_extensions (egl, egl_device, NULL, + "EGL_EXT_device_drm_render_node", + NULL)) + { + if (!meta_egl_query_device_string (egl, egl_device, + EGL_DRM_RENDER_NODE_FILE_EXT, + &device_path, error)) + return NULL; + } + + if (!device_path && + meta_egl_egl_device_has_extensions (egl, egl_device, NULL, + "EGL_EXT_device_drm", + NULL)) + { + if (!meta_egl_query_device_string (egl, egl_device, + EGL_DRM_DEVICE_FILE_EXT, + &device_path, error)) + return NULL; + } + + if (!device_path) + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "Failed to find EGL device to initialize linux-drm-syncobj-v1"); + return NULL; + } + + drm_fd = open (device_path, O_RDWR | O_CLOEXEC); + if (drm_fd < 0) + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "Failed to open DRM device %s", + device_path); + return NULL; + } + + if (drmGetCap (drm_fd, DRM_CAP_SYNCOBJ_TIMELINE, &timeline_supported) != 0 + || !timeline_supported) + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "Failed to check DRM syncobj timeline capability"); + return NULL; + } + +#ifdef HAVE_EVENTFD + if (drmSyncobjEventfd (drm_fd, 0, 0, -1, 0) != -1 || errno != ENOENT) +#endif + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "drmSyncobjEventfd failed: linux-drm-syncobj requires eventfd support"); + return NULL; + } + + drm_syncobj_manager = g_object_new (META_TYPE_WAYLAND_DRM_SYNCOBJ_MANAGER, NULL); + drm_syncobj_manager->drm = g_steal_fd (&drm_fd); + + if (!wl_global_create (compositor->wayland_display, + &wp_linux_drm_syncobj_manager_v1_interface, + 1, + drm_syncobj_manager, + drm_syncobj_manager_bind)) + { + g_error ("Failed to create wp_linux_drm_syncobj_manager_v1_interface global"); + } + + return drm_syncobj_manager; +} + +void +meta_wayland_drm_syncobj_init (MetaWaylandCompositor *compositor) +{ + g_autoptr (GError) error = NULL; + MetaWaylandDrmSyncobjManager *manager = + meta_wayland_drm_syncobj_manager_new (compositor, &error); + + if (!manager) + { + if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED)) + { + meta_topic (META_DEBUG_WAYLAND, "Disabling explicit sync: %s", + error->message); + } + else + { + g_warning ("Failed to create linux-drm-syncobj-manager: %s", + error->message); + } + return; + } + + g_object_set_data_full (G_OBJECT (compositor), "-meta-wayland-drm-syncobj-manager", + manager, + g_object_unref); +} + +/* + * Validate that the appropriate acquire and release points have been set + * for this surface. + */ +bool +meta_wayland_surface_explicit_sync_validate (MetaWaylandSurface *surface, + MetaWaylandSurfaceState *state) +{ + MetaWaylandSyncobjSurface *syncobj_surface = g_object_get_qdata (G_OBJECT (surface), + quark_syncobj_surface); + + if (!syncobj_surface) + return TRUE; + + if (state->buffer) + { + if (state->buffer->type != META_WAYLAND_BUFFER_TYPE_DMA_BUF) + { + wl_resource_post_error (syncobj_surface->resource, + WP_LINUX_DRM_SYNCOBJ_SURFACE_V1_ERROR_UNSUPPORTED_BUFFER, + "Explicit Sync only supported on dmabuf buffers"); + return FALSE; + } + + if (!state->drm_syncobj.acquire) + { + wl_resource_post_error (syncobj_surface->resource, + WP_LINUX_DRM_SYNCOBJ_SURFACE_V1_ERROR_NO_ACQUIRE_POINT, + "No Acquire point provided"); + return FALSE; + } + + if (!state->drm_syncobj.release) + { + wl_resource_post_error (syncobj_surface->resource, + WP_LINUX_DRM_SYNCOBJ_SURFACE_V1_ERROR_NO_RELEASE_POINT, + "No Release point provided"); + return FALSE; + } + + if (state->drm_syncobj.acquire->timeline == state->drm_syncobj.release->timeline && + state->drm_syncobj.acquire->sync_point >= state->drm_syncobj.release->sync_point) + { + wl_resource_post_error (syncobj_surface->resource, + WP_LINUX_DRM_SYNCOBJ_SURFACE_V1_ERROR_CONFLICTING_POINTS, + "Invalid Release and Acquire point combination"); + return FALSE; + } + } + else if (state->drm_syncobj.acquire || state->drm_syncobj.release) + { + wl_resource_post_error (syncobj_surface->resource, + WP_LINUX_DRM_SYNCOBJ_SURFACE_V1_ERROR_NO_BUFFER, + "Release or Acquire point set but no buffer attached"); + return FALSE; + } + + return TRUE; +} diff --git a/src/wayland/meta-wayland-linux-drm-syncobj.h b/src/wayland/meta-wayland-linux-drm-syncobj.h new file mode 100644 index 000000000..cfe361b7c --- /dev/null +++ b/src/wayland/meta-wayland-linux-drm-syncobj.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2023 NVIDIA Corporation. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + * Written by: + * Austin Shafer + */ + +#pragma once + +#include + +#include "wayland/meta-wayland-types.h" +#include "wayland/meta-drm-timeline.h" + +#include "linux-drm-syncobj-v1-server-protocol.h" + +#define META_TYPE_WAYLAND_SYNC_POINT (meta_wayland_sync_point_get_type ()) +G_DECLARE_FINAL_TYPE (MetaWaylandSyncPoint, + meta_wayland_sync_point, + META, WAYLAND_SYNC_POINT, + GObject) + +typedef struct _MetaWaylandSyncPoint { + GObject parent; + + MetaWaylandSyncobjTimeline *timeline; + uint64_t sync_point; +} MetaWaylandSyncPoint; + +bool +meta_wayland_surface_explicit_sync_validate (MetaWaylandSurface *surface, + MetaWaylandSurfaceState *state); + +void +meta_wayland_drm_syncobj_init (MetaWaylandCompositor *compositor); + +gboolean +meta_wayland_sync_timeline_set_sync_point (MetaWaylandSyncobjTimeline *timeline, + uint64_t sync_point, + int sync_fd, + GError **error); + +int +meta_wayland_sync_timeline_get_eventfd (MetaWaylandSyncobjTimeline *timeline, + uint64_t sync_point, + GError **error); diff --git a/src/wayland/meta-wayland-surface-private.h b/src/wayland/meta-wayland-surface-private.h index 2b61a2fc8..e3a88c0a3 100644 --- a/src/wayland/meta-wayland-surface-private.h +++ b/src/wayland/meta-wayland-surface-private.h @@ -29,6 +29,7 @@ #include "meta/meta-wayland-surface.h" #include "wayland/meta-wayland-pointer-constraints.h" #include "wayland/meta-wayland-types.h" +#include "wayland/meta-wayland-linux-drm-syncobj.h" #define META_TYPE_WAYLAND_SURFACE_ROLE (meta_wayland_surface_role_get_type ()) G_DECLARE_DERIVABLE_TYPE (MetaWaylandSurfaceRole, meta_wayland_surface_role, @@ -128,6 +129,12 @@ struct _MetaWaylandSurfaceState /* xdg_popup */ MetaWaylandXdgPositioner *xdg_positioner; uint32_t xdg_popup_reposition_token; + + /* Explicit Synchronization */ + struct { + MetaWaylandSyncPoint *acquire; + MetaWaylandSyncPoint *release; + } drm_syncobj; }; struct _MetaWaylandDragDestFuncs diff --git a/src/wayland/meta-wayland-surface.c b/src/wayland/meta-wayland-surface.c index b132504d3..6dc5006b7 100644 --- a/src/wayland/meta-wayland-surface.c +++ b/src/wayland/meta-wayland-surface.c @@ -48,6 +48,7 @@ #include "wayland/meta-wayland-viewporter.h" #include "wayland/meta-wayland-xdg-shell.h" #include "wayland/meta-window-wayland.h" +#include "wayland/meta-wayland-linux-drm-syncobj.h" #ifdef HAVE_XWAYLAND #include "wayland/meta-xwayland-private.h" @@ -446,6 +447,9 @@ meta_wayland_surface_state_set_default (MetaWaylandSurfaceState *state) wl_list_init (&state->presentation_feedback_list); state->xdg_popup_reposition_token = 0; + + state->drm_syncobj.acquire = NULL; + state->drm_syncobj.release = NULL; } static void @@ -466,6 +470,8 @@ meta_wayland_surface_state_clear (MetaWaylandSurfaceState *state) MetaWaylandFrameCallback *cb, *next; g_clear_object (&state->texture); + g_clear_object (&state->drm_syncobj.acquire); + g_clear_object (&state->drm_syncobj.release); g_clear_pointer (&state->surface_damage, mtk_region_unref); g_clear_pointer (&state->buffer_damage, mtk_region_unref); @@ -630,6 +636,11 @@ meta_wayland_surface_state_merge_into (MetaWaylandSurfaceState *from, to->xdg_positioner = g_steal_pointer (&from->xdg_positioner); to->xdg_popup_reposition_token = from->xdg_popup_reposition_token; } + + g_set_object (&to->drm_syncobj.acquire, from->drm_syncobj.acquire); + g_clear_object (&from->drm_syncobj.acquire); + g_set_object (&to->drm_syncobj.release, from->drm_syncobj.release); + g_clear_object (&from->drm_syncobj.release); } static void @@ -914,6 +925,7 @@ meta_wayland_surface_commit (MetaWaylandSurface *surface) MetaWaylandBuffer *buffer = pending->buffer; MetaWaylandTransaction *transaction; MetaWaylandSurface *subsurface_surface; + MetaWaylandSyncPoint *release_point = pending->drm_syncobj.release; COGL_TRACE_BEGIN_SCOPED (MetaWaylandSurfaceCommit, "Meta::WaylandSurface::commit()"); @@ -921,6 +933,9 @@ meta_wayland_surface_commit (MetaWaylandSurface *surface) if (pending->scale > 0) surface->committed_state.scale = pending->scale; + if (!meta_wayland_surface_explicit_sync_validate (surface, pending)) + return; + if (buffer) { g_autoptr (GError) error = NULL; @@ -946,6 +961,9 @@ meta_wayland_surface_commit (MetaWaylandSurface *surface) pending->texture = g_object_ref (surface->committed_state.texture); + if (release_point) + g_ptr_array_add (buffer->release_points, g_object_ref (release_point)); + g_object_ref (buffer); meta_wayland_buffer_inc_use_count (buffer); } diff --git a/src/wayland/meta-wayland-transaction.c b/src/wayland/meta-wayland-transaction.c index 70f2b4047..694ce6dbe 100644 --- a/src/wayland/meta-wayland-transaction.c +++ b/src/wayland/meta-wayland-transaction.c @@ -26,6 +26,7 @@ #include "wayland/meta-wayland.h" #include "wayland/meta-wayland-buffer.h" #include "wayland/meta-wayland-dma-buf.h" +#include "wayland/meta-wayland-linux-drm-syncobj.h" #define META_WAYLAND_TRANSACTION_NONE ((void *)(uintptr_t) G_MAXSIZE) @@ -314,6 +315,17 @@ meta_wayland_transaction_dma_buf_dispatch (MetaWaylandBuffer *buffer, meta_wayland_transaction_maybe_apply (transaction); } +static void +ensure_buf_sources (MetaWaylandTransaction *transaction) +{ + if (!transaction->buf_sources) + { + transaction->buf_sources = + g_hash_table_new_full (NULL, NULL, NULL, + (GDestroyNotify) g_source_destroy); + } +} + static gboolean meta_wayland_transaction_add_dma_buf_source (MetaWaylandTransaction *transaction, MetaWaylandBuffer *buffer) @@ -330,12 +342,35 @@ meta_wayland_transaction_add_dma_buf_source (MetaWaylandTransaction *transaction if (!source) return FALSE; - if (!transaction->buf_sources) - { - transaction->buf_sources = - g_hash_table_new_full (NULL, NULL, NULL, - (GDestroyNotify) g_source_destroy); - } + ensure_buf_sources (transaction); + + g_hash_table_insert (transaction->buf_sources, buffer, source); + g_source_attach (source, NULL); + g_source_unref (source); + + return TRUE; +} + +static gboolean +meta_wayland_transaction_add_drm_syncobj_source (MetaWaylandTransaction *transaction, + MetaWaylandBuffer *buffer, + MetaWaylandSyncPoint *acquire) +{ + GSource *source; + + if (transaction->buf_sources && + g_hash_table_contains (transaction->buf_sources, buffer)) + return FALSE; + + source = meta_wayland_drm_syncobj_create_source (buffer, + acquire->timeline, + acquire->sync_point, + meta_wayland_transaction_dma_buf_dispatch, + transaction); + if (!source) + return FALSE; + + ensure_buf_sources (transaction); g_hash_table_insert (transaction->buf_sources, buffer, source); g_source_attach (source, NULL); @@ -382,8 +417,11 @@ meta_wayland_transaction_commit (MetaWaylandTransaction *transaction) { MetaWaylandBuffer *buffer = entry->state->buffer; - if (buffer && - meta_wayland_transaction_add_dma_buf_source (transaction, buffer)) + if ((entry->state->drm_syncobj.acquire && + meta_wayland_transaction_add_drm_syncobj_source (transaction, buffer, + entry->state->drm_syncobj.acquire)) + || (buffer && + meta_wayland_transaction_add_dma_buf_source (transaction, buffer))) maybe_apply = FALSE; if (entry->state->subsurface_placement_ops) diff --git a/src/wayland/meta-wayland-types.h b/src/wayland/meta-wayland-types.h index 40c3bb733..4f224a900 100644 --- a/src/wayland/meta-wayland-types.h +++ b/src/wayland/meta-wayland-types.h @@ -60,6 +60,8 @@ typedef struct _MetaWaylandActivation MetaWaylandActivation; typedef struct _MetaWaylandDmaBufManager MetaWaylandDmaBufManager; +typedef struct _MetaWaylandSyncobjTimeline MetaWaylandSyncobjTimeline; + typedef struct _MetaWaylandXdgPositioner MetaWaylandXdgPositioner; typedef struct _MetaXWaylandManager MetaXWaylandManager; diff --git a/src/wayland/meta-wayland.c b/src/wayland/meta-wayland.c index 7a24cf6b4..501b69a91 100644 --- a/src/wayland/meta-wayland.c +++ b/src/wayland/meta-wayland.c @@ -57,6 +57,7 @@ #include "wayland/meta-wayland-tablet-manager.h" #include "wayland/meta-wayland-transaction.h" #include "wayland/meta-wayland-xdg-foreign.h" +#include "wayland/meta-wayland-linux-drm-syncobj.h" #ifdef HAVE_XWAYLAND #include "wayland/meta-wayland-x11-interop.h" @@ -868,6 +869,7 @@ meta_wayland_compositor_new (MetaContext *context) meta_wayland_activation_init (compositor); meta_wayland_transaction_init (compositor); meta_wayland_idle_inhibit_init (compositor); + meta_wayland_drm_syncobj_init (compositor); #ifdef HAVE_WAYLAND_EGLSTREAM {