From 8e9f01d1cef3692aeb3fa9de140ee1207df768ba Mon Sep 17 00:00:00 2001 From: Austin Shafer Date: Wed, 6 Sep 2023 13:06:54 -0400 Subject: [PATCH] wayland: Add MetaDrmTimeline This abstracts away directly dealing with DRM syncobjs. Explicit sync code can simply create a MetaDrmTimeline and request an fd at a particular sync point. Part-of: --- config.h.meson | 3 + meson.build | 3 + src/meson.build | 2 + src/wayland/meta-drm-timeline.c | 269 ++++++++++++++++++++++++++++++++ src/wayland/meta-drm-timeline.h | 47 ++++++ 5 files changed, 324 insertions(+) create mode 100644 src/wayland/meta-drm-timeline.c create mode 100644 src/wayland/meta-drm-timeline.h diff --git a/config.h.meson b/config.h.meson index 094d79616..9919bd4d0 100644 --- a/config.h.meson +++ b/config.h.meson @@ -135,3 +135,6 @@ /* Supports malloc_trim */ #mesondefine HAVE_MALLOC_TRIM + +/* Supports eventfd */ +#mesondefine HAVE_EVENTFD diff --git a/meson.build b/meson.build index c5ec57395..9732d8460 100644 --- a/meson.build +++ b/meson.build @@ -541,6 +541,8 @@ endif cc.compiles('void main (void) { __builtin_ffsl (0); __builtin_popcountl (0); }') +have_eventfd = cc.has_header('sys/eventfd.h') + cdata = configuration_data() cdata.set_quoted('GETTEXT_PACKAGE', gettext_package) cdata.set_quoted('VERSION', meson.project_version()) @@ -573,6 +575,7 @@ cdata.set('HAVE_LIBDISPLAY_INFO', have_libdisplay_info) cdata.set('HAVE_PANGO_FT2', have_pango_ft2) cdata.set('HAVE_TIMERFD', have_timerfd) cdata.set('HAVE_MALLOC_TRIM', have_malloc_trim) +cdata.set('HAVE_EVENTFD', have_eventfd) if have_x11_client xkb_base = xkeyboard_config_dep.get_variable('xkb_base') diff --git a/src/meson.build b/src/meson.build index a8cf7e197..b7cc408c5 100644 --- a/src/meson.build +++ b/src/meson.build @@ -579,6 +579,8 @@ if have_wayland 'core/meta-service-channel.h', 'wayland/meta-cursor-sprite-wayland.c', 'wayland/meta-cursor-sprite-wayland.h', + 'wayland/meta-drm-timeline.c', + 'wayland/meta-drm-timeline.h', 'wayland/meta-pointer-confinement-wayland.c', 'wayland/meta-pointer-confinement-wayland.h', 'wayland/meta-pointer-lock-wayland.c', diff --git a/src/wayland/meta-drm-timeline.c b/src/wayland/meta-drm-timeline.c new file mode 100644 index 000000000..6d86fd19f --- /dev/null +++ b/src/wayland/meta-drm-timeline.c @@ -0,0 +1,269 @@ +/* + * 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 + */ + +/** + * MetaDrmTimeline + * + * MetaDrmTimeline is a helper for handling DRM syncobj operations. It + * can import DRM syncobjs and export eventfds at a particular point. + * + * This is heavily inspired by wlroot's wlr_render_timeline, written by + * Simon Ser. + */ + +#include "config.h" + +#include +#include +#include +#ifdef HAVE_EVENTFD +#include +#endif + +#include "meta/util.h" +#include "wayland/meta-drm-timeline.h" + +enum +{ + PROP_0, + + PROP_DRM_FD, + PROP_SYNCOBJ_FD, + + N_PROPS +}; + +typedef struct _MetaDrmTimeline +{ + GObject parent; + + int drm; + int drm_syncobj_fd; + uint32_t drm_syncobj; +} MetaDrmTimeline; + +static GParamSpec *obj_props[N_PROPS]; + +static void initable_iface_init (GInitableIface *initable_iface); + +G_DEFINE_FINAL_TYPE_WITH_CODE (MetaDrmTimeline, meta_drm_timeline, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, + initable_iface_init)) + +static void +meta_drm_timeline_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + MetaDrmTimeline *timeline = META_DRM_TIMELINE (object); + + switch (prop_id) + { + case PROP_DRM_FD: + g_value_set_int (value, timeline->drm); + break; + case PROP_SYNCOBJ_FD: + g_value_set_int (value, timeline->drm_syncobj_fd); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +meta_drm_timeline_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + MetaDrmTimeline *timeline = META_DRM_TIMELINE (object); + int fd; + + switch (prop_id) + { + case PROP_DRM_FD: + fd = g_value_get_int (value); + timeline->drm = fcntl (fd, F_DUPFD_CLOEXEC, 0); + break; + case PROP_SYNCOBJ_FD: + fd = g_value_get_int (value); + timeline->drm_syncobj_fd = fcntl (fd, F_DUPFD_CLOEXEC, 0); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static gboolean +meta_drm_timeline_initable_init (GInitable *initable, + GCancellable *cancellable, + GError **error) +{ + MetaDrmTimeline *timeline = META_DRM_TIMELINE (initable); + + if (drmSyncobjFDToHandle (timeline->drm, + timeline->drm_syncobj_fd, + &timeline->drm_syncobj) != 0) + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "Failed to import DRM syncobj"); + return FALSE; + } + + return TRUE; +} + +static void +initable_iface_init (GInitableIface *initable_iface) +{ + initable_iface->init = meta_drm_timeline_initable_init; +} + +MetaDrmTimeline * +meta_drm_timeline_import_syncobj (int fd, + int drm_syncobj, + GError **error) +{ + MetaDrmTimeline *timeline = g_initable_new (META_TYPE_DRM_TIMELINE, + NULL, error, + "drm-fd", fd, + "syncobj-fd", drm_syncobj, + NULL); + + return timeline; +} + +int +meta_drm_timeline_get_eventfd (MetaDrmTimeline *timeline, + uint64_t sync_point, + GError **error) +{ + g_autofd int fd = -1; + +#ifdef HAVE_EVENTFD + fd = eventfd (0, EFD_CLOEXEC); + if (fd < 0) + return -1; + + if (drmSyncobjEventfd (timeline->drm, timeline->drm_syncobj, + sync_point, fd, 0) != 0) + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "DRM_IOCTL_SYNCOBJ_EVENTFD: Failed to export eventfd"); + return -1; + } +#endif + + return g_steal_fd (&fd); +} + +gboolean +meta_drm_timeline_set_sync_point (MetaDrmTimeline *timeline, + uint64_t sync_point, + int sync_fd, + GError **error) +{ + uint32_t tmp; + + /* Import our syncfd at a new release point */ + if (drmSyncobjCreate (timeline->drm, 0, &tmp) != 0) + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "Failed to create temporary syncobj"); + return FALSE; + } + + if (drmSyncobjImportSyncFile (timeline->drm, tmp, sync_fd) != 0) + goto end; + + if (drmSyncobjTransfer (timeline->drm, timeline->drm_syncobj, + sync_point, tmp, 0, 0) != 0) + goto end; + + drmSyncobjDestroy (timeline->drm, tmp); + return TRUE; + +end: + drmSyncobjDestroy (timeline->drm, tmp); + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "Failed to import syncfd at specified point"); + return FALSE; +} + +static void +meta_drm_timeline_finalize (GObject *object) +{ + MetaDrmTimeline *timeline = META_DRM_TIMELINE (object); + + drmSyncobjDestroy (timeline->drm, timeline->drm_syncobj); + g_clear_fd (&timeline->drm_syncobj_fd, NULL); + g_clear_fd (&timeline->drm, NULL); + + G_OBJECT_CLASS (meta_drm_timeline_parent_class)->finalize (object); +} + +static void +meta_drm_timeline_init (MetaDrmTimeline *timeline) +{ + timeline->drm = -1; + timeline->drm_syncobj_fd = -1; + timeline->drm_syncobj = -1; +} + +static void +meta_drm_timeline_class_init (MetaDrmTimelineClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->get_property = meta_drm_timeline_get_property; + object_class->set_property = meta_drm_timeline_set_property; + object_class->finalize = meta_drm_timeline_finalize; + + obj_props[PROP_DRM_FD] = + g_param_spec_int ("drm-fd", + NULL, + NULL, + 0, INT_MAX, 0, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + + obj_props[PROP_SYNCOBJ_FD] = + g_param_spec_int ("syncobj-fd", + NULL, + NULL, + 0, INT_MAX, 0, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties (object_class, N_PROPS, obj_props); +} diff --git a/src/wayland/meta-drm-timeline.h b/src/wayland/meta-drm-timeline.h new file mode 100644 index 000000000..8206e3066 --- /dev/null +++ b/src/wayland/meta-drm-timeline.h @@ -0,0 +1,47 @@ +/* + * 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 +#include + +#define META_TYPE_DRM_TIMELINE (meta_drm_timeline_get_type ()) +G_DECLARE_FINAL_TYPE (MetaDrmTimeline, meta_drm_timeline, + META, DRM_TIMELINE, GObject); + +typedef struct _MetaDrmTimeline MetaDrmTimeline; + +MetaDrmTimeline * meta_drm_timeline_create (int fd, + GError **error); + +MetaDrmTimeline * meta_drm_timeline_import_syncobj (int fd, + int drm_syncobj, + GError **error); + +int meta_drm_timeline_get_eventfd (MetaDrmTimeline *timeline, + uint64_t sync_point, + GError **error); + +gboolean meta_drm_timeline_set_sync_point (MetaDrmTimeline *timeline, + uint64_t sync_point, + int sync_fd, + GError **error);