File mutter-implement-linux-drm-syncobj-v1.patch of Package mutter
diff --git a/cogl/cogl/cogl-context-private.h b/cogl/cogl/cogl-context-private.h
index 502dda72b..dca006653 100644
--- a/cogl/cogl/cogl-context-private.h
+++ b/cogl/cogl/cogl-context-private.h
@@ -311,3 +311,6 @@ _cogl_context_set_current_projection_entry (CoglContext *context,
void
_cogl_context_set_current_modelview_entry (CoglContext *context,
CoglMatrixEntry *entry);
+
+void
+_cogl_context_update_sync (CoglContext *context);
diff --git a/cogl/cogl/cogl-context.c b/cogl/cogl/cogl-context.c
index f7d276825..7df82a93f 100644
--- a/cogl/cogl/cogl-context.c
+++ b/cogl/cogl/cogl-context.c
@@ -458,6 +458,28 @@ _cogl_context_set_current_modelview_entry (CoglContext *context,
context->current_modelview_entry = entry;
}
+void
+_cogl_context_update_sync (CoglContext *context)
+{
+ const CoglWinsysVtable *winsys = _cogl_context_get_winsys (context);
+
+ if (!winsys->update_sync)
+ return;
+
+ winsys->update_sync (context);
+}
+
+int
+cogl_context_get_latest_sync_fd (CoglContext *context)
+{
+ const CoglWinsysVtable *winsys = _cogl_context_get_winsys (context);
+
+ if (!winsys->get_sync_fd)
+ return -1;
+
+ return winsys->get_sync_fd (context);
+}
+
CoglGraphicsResetStatus
cogl_get_graphics_reset_status (CoglContext *context)
{
diff --git a/cogl/cogl/cogl-context.h b/cogl/cogl/cogl-context.h
index 8ad691420..847da576a 100644
--- a/cogl/cogl/cogl-context.h
+++ b/cogl/cogl/cogl-context.h
@@ -368,4 +368,18 @@ cogl_context_timestamp_query_get_time_ns (CoglContext *context,
COGL_EXPORT int64_t
cogl_context_get_gpu_time_ns (CoglContext *context);
+/**
+ * cogl_context_get_latest_sync_fd
+ * @context: a #CoglContext pointer
+ *
+ * This function is used to get support for waiting on previous
+ * GPU work through sync fds. It will return a sync fd which will
+ * signal when the previous work has completed.
+ *
+ * Return value: sync fd for latest GPU submission if available,
+ * returns -1 if not.
+ */
+COGL_EXPORT int
+cogl_context_get_latest_sync_fd (CoglContext *context);
+
G_END_DECLS
diff --git a/cogl/cogl/winsys/cogl-onscreen-egl.c b/cogl/cogl/winsys/cogl-onscreen-egl.c
index 49514ff23..e7cea0443 100644
--- a/cogl/cogl/winsys/cogl-onscreen-egl.c
+++ b/cogl/cogl/winsys/cogl-onscreen-egl.c
@@ -309,6 +309,9 @@ cogl_onscreen_egl_swap_buffers_with_damage (CoglOnscreen *onscreen,
COGL_FRAMEBUFFER (onscreen),
COGL_FRAMEBUFFER_STATE_BIND);
+ /* Update our "latest" sync fd to contain all previous work */
+ _cogl_context_update_sync (context);
+
if (cogl_has_feature (context, COGL_FEATURE_ID_TIMESTAMP_QUERY))
{
info->gpu_time_before_buffer_swap_ns =
diff --git a/cogl/cogl/winsys/cogl-winsys-egl-feature-functions.h b/cogl/cogl/winsys/cogl-winsys-egl-feature-functions.h
index 494d5ea39..105055c1a 100644
--- a/cogl/cogl/winsys/cogl-winsys-egl-feature-functions.h
+++ b/cogl/cogl/winsys/cogl-winsys-egl-feature-functions.h
@@ -156,6 +156,15 @@ COGL_WINSYS_FEATURE_FUNCTION (EGLBoolean, eglDestroySync,
(EGLDisplay dpy,
EGLSyncKHR sync))
COGL_WINSYS_FEATURE_END ()
+
+COGL_WINSYS_FEATURE_BEGIN (native_fence_sync,
+ "ANDROID\0",
+ "native_fence_sync\0",
+ COGL_EGL_WINSYS_FEATURE_NATIVE_FENCE_SYNC)
+COGL_WINSYS_FEATURE_FUNCTION (EGLint, eglDupNativeFenceFD,
+ (EGLDisplay dpy,
+ EGLSyncKHR sync))
+COGL_WINSYS_FEATURE_END ()
#endif
COGL_WINSYS_FEATURE_BEGIN (surfaceless_context,
diff --git a/cogl/cogl/winsys/cogl-winsys-egl-private.h b/cogl/cogl/winsys/cogl-winsys-egl-private.h
index c1136c6d8..39b37d8a5 100644
--- a/cogl/cogl/winsys/cogl-winsys-egl-private.h
+++ b/cogl/cogl/winsys/cogl-winsys-egl-private.h
@@ -101,6 +101,7 @@ typedef enum _CoglEGLWinsysFeature
COGL_EGL_WINSYS_FEATURE_FENCE_SYNC =1L<<5,
COGL_EGL_WINSYS_FEATURE_SURFACELESS_CONTEXT =1L<<6,
COGL_EGL_WINSYS_FEATURE_CONTEXT_PRIORITY =1L<<7,
+ COGL_EGL_WINSYS_FEATURE_NATIVE_FENCE_SYNC =1L<<9,
} CoglEGLWinsysFeature;
typedef struct _CoglRendererEGL
@@ -119,6 +120,9 @@ typedef struct _CoglRendererEGL
/* vtable for platform specific parts */
const CoglWinsysEGLVtable *platform_vtable;
+ /* Sync for latest submitted work */
+ EGLSyncKHR sync;
+
/* Function pointers for EGL specific extensions */
#define COGL_WINSYS_FEATURE_BEGIN(a, b, c, d)
diff --git a/cogl/cogl/winsys/cogl-winsys-egl-x11.c b/cogl/cogl/winsys/cogl-winsys-egl-x11.c
index af4df1814..c3fce8f92 100644
--- a/cogl/cogl/winsys/cogl-winsys-egl-x11.c
+++ b/cogl/cogl/winsys/cogl-winsys-egl-x11.c
@@ -243,6 +243,7 @@ _cogl_winsys_renderer_connect (CoglRenderer *renderer,
xlib_renderer = _cogl_xlib_renderer_get_data (renderer);
egl_renderer->platform_vtable = &_cogl_winsys_egl_vtable;
+ egl_renderer->sync = EGL_NO_SYNC_KHR;
if (!_cogl_xlib_renderer_connect (renderer, error))
goto error;
diff --git a/cogl/cogl/winsys/cogl-winsys-egl.c b/cogl/cogl/winsys/cogl-winsys-egl.c
index 28aa0a268..89a432a6f 100644
--- a/cogl/cogl/winsys/cogl-winsys-egl.c
+++ b/cogl/cogl/winsys/cogl-winsys-egl.c
@@ -482,6 +482,9 @@ _cogl_winsys_display_destroy (CoglDisplay *display)
g_return_if_fail (egl_display != NULL);
+ if (egl_renderer->sync != EGL_NO_SYNC_KHR)
+ egl_renderer->pf_eglDestroySync (egl_renderer->edpy, egl_renderer->sync);
+
cleanup_context (display);
if (egl_renderer->platform_vtable->display_destroy)
@@ -614,6 +617,37 @@ _cogl_winsys_fence_destroy (CoglContext *context, void *fence)
renderer->pf_eglDestroySync (renderer->edpy, fence);
}
+
+static int
+_cogl_winsys_get_sync_fd (CoglContext *context)
+{
+ CoglRendererEGL *renderer = context->display->renderer->winsys;
+ int fd;
+
+ if (!renderer->pf_eglDupNativeFenceFD)
+ return -1;
+
+ fd = renderer->pf_eglDupNativeFenceFD (renderer->edpy, renderer->sync);
+ if (fd == EGL_NO_NATIVE_FENCE_FD_ANDROID)
+ return -1;
+
+ return fd;
+}
+
+static void
+_cogl_winsys_update_sync (CoglContext *context)
+{
+ CoglRendererEGL *renderer = context->display->renderer->winsys;
+
+ if (!renderer->pf_eglDestroySync || !renderer->pf_eglCreateSync)
+ return;
+
+ if (renderer->sync != EGL_NO_SYNC_KHR)
+ renderer->pf_eglDestroySync (renderer->edpy, renderer->sync);
+
+ renderer->sync = renderer->pf_eglCreateSync (renderer->edpy,
+ EGL_SYNC_NATIVE_FENCE_ANDROID, NULL);
+}
#endif
static CoglWinsysVtable _cogl_winsys_vtable =
@@ -636,6 +670,8 @@ static CoglWinsysVtable _cogl_winsys_vtable =
.fence_add = _cogl_winsys_fence_add,
.fence_is_complete = _cogl_winsys_fence_is_complete,
.fence_destroy = _cogl_winsys_fence_destroy,
+ .get_sync_fd = _cogl_winsys_get_sync_fd,
+ .update_sync = _cogl_winsys_update_sync,
#endif
};
diff --git a/cogl/cogl/winsys/cogl-winsys-private.h b/cogl/cogl/winsys/cogl-winsys-private.h
index d414cada0..e40c6b1b4 100644
--- a/cogl/cogl/winsys/cogl-winsys-private.h
+++ b/cogl/cogl/winsys/cogl-winsys-private.h
@@ -144,6 +144,12 @@ typedef struct _CoglWinsysVtable
(*fence_destroy) (CoglContext *ctx,
void *fence);
+ void
+ (*update_sync) (CoglContext *ctx);
+
+ int
+ (*get_sync_fd) (CoglContext *ctx);
+
} CoglWinsysVtable;
typedef const CoglWinsysVtable *(*CoglWinsysVtableGetter) (void);
diff --git a/config.h.meson b/config.h.meson
index d319aa344..517f20837 100644
--- a/config.h.meson
+++ b/config.h.meson
@@ -115,3 +115,6 @@
/* Whether the Xwayland has -enable-ei-portal option */
#mesondefine HAVE_XWAYLAND_ENABLE_EI_PORTAL
+
+/* Supports eventfd */
+#mesondefine HAVE_EVENTFD
diff --git a/meson.build b/meson.build
index 5530cebe7..ed131266a 100644
--- a/meson.build
+++ b/meson.build
@@ -516,6 +516,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())
@@ -541,6 +543,7 @@ cdata.set('HAVE_STARTUP_NOTIFICATION', have_startup_notification)
cdata.set('HAVE_INTROSPECTION', have_introspection)
cdata.set('HAVE_PROFILER', have_profiler)
cdata.set('HAVE_LIBDISPLAY_INFO', have_libdisplay_info)
+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 ca2ef166c..d4bfb143a 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -589,6 +589,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',
@@ -641,6 +643,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',
@@ -1080,6 +1084,7 @@ if have_wayland
['xdg-output', 'unstable', 'v1', ],
['xdg-shell', 'stable', ],
['xwayland-keyboard-grab', 'unstable', 'v1', ],
+ ['linux-drm-syncobj-v1', 'private', ],
]
if have_wayland_eglstream
wayland_eglstream_protocols_dir = wayland_eglstream_protocols_dep.get_variable('pkgdatadir')
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 <http://www.gnu.org/licenses/>.
+ *
+ * Written by:
+ * Austin Shafer <ashafer@nvidia.com>
+ */
+
+/**
+ * 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 <fcntl.h>
+#include <xf86drm.h>
+#include <glib/gstdio.h>
+#ifdef HAVE_EVENTFD
+#include <sys/eventfd.h>
+#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 <http://www.gnu.org/licenses/>.
+ *
+ * Written by:
+ * Austin Shafer <ashafer@nvidia.com>
+ */
+
+#pragma once
+
+#include <glib.h>
+#include <glib-object.h>
+#include <stdint.h>
+
+#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);
diff --git a/src/wayland/meta-wayland-buffer.c b/src/wayland/meta-wayland-buffer.c
index 7a9858010..ae928e843 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 <drm_fourcc.h>
+#include <glib/gstdio.h>
#include "backends/meta-backend-private.h"
#include "clutter/clutter.h"
@@ -56,6 +57,8 @@
#include "meta/util.h"
#include "wayland/meta-wayland-dma-buf.h"
#include "wayland/meta-wayland-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"
@@ -661,12 +664,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
@@ -875,6 +909,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
@@ -893,6 +928,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 2b50c2539..2ce3a2c9b 100644
--- a/src/wayland/meta-wayland-buffer.h
+++ b/src/wayland/meta-wayland-buffer.h
@@ -80,6 +80,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 79a0f1409..bd155cdae 100644
--- a/src/wayland/meta-wayland-dma-buf.c
+++ b/src/wayland/meta-wayland-dma-buf.c
@@ -39,6 +39,8 @@
#include <drm_fourcc.h>
#include <glib/gstdio.h>
+#include <linux/dma-buf.h>
+#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
@@ -54,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"
@@ -846,10 +849,11 @@ typedef struct _MetaWaylandDmaBufSource
gpointer user_data;
gpointer fd_tags[META_WAYLAND_DMA_BUF_MAX_FDS];
+ int owned_sync_fd[META_WAYLAND_DMA_BUF_MAX_FDS];
} MetaWaylandDmaBufSource;
static gboolean
-meta_wayland_dma_buf_fd_readable (int fd)
+is_fd_readable (int fd)
{
GPollFD poll_fd;
@@ -880,11 +884,16 @@ meta_wayland_dma_buf_source_dispatch (GSource *base,
for (i = 0; i < META_WAYLAND_DMA_BUF_MAX_FDS; i++)
{
gpointer fd_tag = source->fd_tags[i];
+ int fd;
if (!fd_tag)
continue;
- if (!meta_wayland_dma_buf_fd_readable (dma_buf->fds[i]))
+ fd = source->owned_sync_fd[i];
+ if (fd < 0)
+ fd = dma_buf->fds[i];
+
+ if (!is_fd_readable (fd))
{
ready = FALSE;
continue;
@@ -892,6 +901,7 @@ meta_wayland_dma_buf_source_dispatch (GSource *base,
g_source_remove_unix_fd (&source->base, fd_tag);
source->fd_tags[i] = NULL;
+ g_clear_fd (&source->owned_sync_fd[i], NULL);
}
if (!ready)
@@ -918,6 +928,7 @@ meta_wayland_dma_buf_source_finalize (GSource *base)
{
g_source_remove_unix_fd (&source->base, fd_tag);
source->fd_tags[i] = NULL;
+ g_clear_fd (&source->owned_sync_fd[i], NULL);
}
}
@@ -929,6 +940,47 @@ static GSourceFuncs meta_wayland_dma_buf_source_funcs = {
.finalize = meta_wayland_dma_buf_source_finalize
};
+static MetaWaylandDmaBufSource *
+create_source (MetaWaylandBuffer *buffer,
+ MetaWaylandDmaBufSourceDispatch dispatch,
+ gpointer user_data)
+{
+ MetaWaylandDmaBufSource *source;
+ int i;
+
+ source =
+ (MetaWaylandDmaBufSource *) g_source_new (&meta_wayland_dma_buf_source_funcs,
+ sizeof (*source));
+ g_source_set_name ((GSource *) source, "[mutter] DmaBuf readiness source");
+
+ source->buffer = g_object_ref (buffer);
+ source->dispatch = dispatch;
+ source->user_data = user_data;
+
+ for (i = 0; i < META_WAYLAND_DMA_BUF_MAX_FDS; i++)
+ source->owned_sync_fd[i] = -1;
+
+ return source;
+}
+
+static int
+get_sync_file (int dma_buf_fd)
+{
+ struct dma_buf_export_sync_file dbesf = { .flags = DMA_BUF_SYNC_READ };
+ int ret;
+
+ do
+ {
+ ret = ioctl (dma_buf_fd, DMA_BUF_IOCTL_EXPORT_SYNC_FILE, &dbesf);
+ }
+ while (ret == -1 && errno == EINTR);
+
+ if (ret == 0)
+ return dbesf.fd;
+
+ return -1;
+}
+
/**
* meta_wayland_dma_buf_create_source:
* @buffer: A #MetaWaylandBuffer object
@@ -962,18 +1014,15 @@ meta_wayland_dma_buf_create_source (MetaWaylandBuffer *buffer,
if (fd < 0)
break;
- if (meta_wayland_dma_buf_fd_readable (fd))
+ if (is_fd_readable (fd))
continue;
if (!source)
- {
- source =
- (MetaWaylandDmaBufSource *) g_source_new (&meta_wayland_dma_buf_source_funcs,
- sizeof (*source));
- source->buffer = g_object_ref (buffer);
- source->dispatch = dispatch;
- source->user_data = user_data;
- }
+ source = create_source (buffer, dispatch, user_data);
+
+ source->owned_sync_fd[i] = get_sync_file (fd);
+ if (source->owned_sync_fd[i] >= 0)
+ fd = source->owned_sync_fd[i];
source->fd_tags[i] = g_source_add_unix_fd (&source->base, fd, G_IO_IN);
}
@@ -984,6 +1033,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 1c03acb32..eb39f9c7c 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 (MetaWaylandDmaBufBuffer *dma_buf,
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..24b908c41
--- /dev/null
+++ b/src/wayland/meta-wayland-linux-drm-syncobj.c
@@ -0,0 +1,638 @@
+/*
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ * Written by:
+ * Austin Shafer <ashafer@nvidia.com>
+ */
+
+#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 "cogl/cogl-egl.h"
+#include <fcntl.h>
+#include <glib/gstdio.h>
+
+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 <http://www.gnu.org/licenses/>.
+ *
+ * Written by:
+ * Austin Shafer <ashafer@nvidia.com>
+ */
+
+#pragma once
+
+#include <glib.h>
+
+#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.c b/src/wayland/meta-wayland-surface.c
index d42f31828..8eaf4c924 100644
--- a/src/wayland/meta-wayland-surface.c
+++ b/src/wayland/meta-wayland-surface.c
@@ -51,6 +51,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"
@@ -421,6 +422,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
@@ -441,6 +445,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, cairo_region_destroy);
g_clear_pointer (&state->buffer_damage, cairo_region_destroy);
@@ -605,6 +611,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
@@ -924,10 +935,14 @@ 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,
"WaylandSurface (commit)");
+ if (!meta_wayland_surface_explicit_sync_validate (surface, pending))
+ return;
+
if (buffer)
{
g_autoptr (GError) error = NULL;
@@ -953,6 +968,9 @@ meta_wayland_surface_commit (MetaWaylandSurface *surface)
pending->texture = g_object_ref (surface->protocol_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-surface.h b/src/wayland/meta-wayland-surface.h
index 3e8db5a87..459e3c04a 100644
--- a/src/wayland/meta-wayland-surface.h
+++ b/src/wayland/meta-wayland-surface.h
@@ -29,6 +29,7 @@
#include "meta/meta-cursor-tracker.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 (meta_wayland_surface_get_type ())
G_DECLARE_FINAL_TYPE (MetaWaylandSurface,
@@ -134,6 +135,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-transaction.c b/src/wayland/meta-wayland-transaction.c
index 9b999bfa9..7750b2e9f 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)
@@ -287,6 +288,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)
@@ -303,12 +315,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);
@@ -335,8 +370,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;
}
}
diff --git a/src/wayland/meta-wayland-types.h b/src/wayland/meta-wayland-types.h
index c14b340f4..2b5a967ab 100644
--- a/src/wayland/meta-wayland-types.h
+++ b/src/wayland/meta-wayland-types.h
@@ -64,6 +64,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 a2bd7d2ab..a3569af53 100644
--- a/src/wayland/meta-wayland.c
+++ b/src/wayland/meta-wayland.c
@@ -50,6 +50,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"
@@ -808,6 +809,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
{
diff --git a/src/wayland/protocol/linux-drm-syncobj-v1.xml b/src/wayland/protocol/linux-drm-syncobj-v1.xml
new file mode 100644
index 000000000..2c491eaf4
--- /dev/null
+++ b/src/wayland/protocol/linux-drm-syncobj-v1.xml
@@ -0,0 +1,261 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<protocol name="linux_drm_syncobj_v1">
+ <copyright>
+ Copyright 2016 The Chromium Authors.
+ Copyright 2017 Intel Corporation
+ Copyright 2018 Collabora, Ltd
+ Copyright 2021 Simon Ser
+
+ Permission is hereby granted, free of charge, to any person obtaining a
+ copy of this software and associated documentation files (the "Software"),
+ to deal in the Software without restriction, including without limitation
+ the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ and/or sell copies of the Software, and to permit persons to whom the
+ Software is furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice (including the next
+ paragraph) shall be included in all copies or substantial portions of the
+ Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ DEALINGS IN THE SOFTWARE.
+ </copyright>
+
+ <description summary="protocol for providing explicit synchronization">
+ This protocol allows clients to request explicit synchronization for
+ buffers. It is tied to the Linux DRM synchronization object framework.
+
+ Synchronization refers to co-ordination of pipelined operations performed
+ on buffers. Most GPU clients will schedule an asynchronous operation to
+ render to the buffer, then immediately send the buffer to the compositor
+ to be attached to a surface.
+
+ With implicit synchronization, ensuring that the rendering operation is
+ complete before the compositor displays the buffer is an implementation
+ detail handled by either the kernel or userspace graphics driver.
+
+ By contrast, with explicit synchronization, DRM synchronization object
+ timeline points mark when the asynchronous operations are complete. When
+ submitting a buffer, the client provides a timeline point which will be
+ waited on before the compositor accesses the buffer, and another timeline
+ point that the compositor will signal when it no longer needs to access the
+ buffer contents for the purposes of the surface commit.
+
+ Linux DRM synchronization objects are documented at:
+ https://dri.freedesktop.org/docs/drm/gpu/drm-mm.html#drm-sync-objects
+
+ Warning! The protocol described in this file is currently in the testing
+ phase. Backward compatible changes may be added together with the
+ corresponding interface version bump. Backward incompatible changes can
+ only be done by creating a new major version of the extension.
+ </description>
+
+ <interface name="wp_linux_drm_syncobj_manager_v1" version="1">
+ <description summary="global for providing explicit synchronization">
+ This global is a factory interface, allowing clients to request
+ explicit synchronization for buffers on a per-surface basis.
+
+ See wp_linux_drm_syncobj_surface_v1 for more information.
+ </description>
+
+ <request name="destroy" type="destructor">
+ <description summary="destroy explicit synchronization factory object">
+ Destroy this explicit synchronization factory object. Other objects
+ shall not be affected by this request.
+ </description>
+ </request>
+
+ <enum name="error">
+ <entry name="surface_exists" value="0"
+ summary="the surface already has a synchronization object associated"/>
+ <entry name="invalid_timeline" value="1"
+ summary="the timeline object could not be imported"/>
+ </enum>
+
+ <request name="get_surface">
+ <description summary="extend surface interface for explicit synchronization">
+ Instantiate an interface extension for the given wl_surface to provide
+ explicit synchronization.
+
+ If the given wl_surface already has an explicit synchronization object
+ associated, the surface_exists protocol error is raised.
+
+ Graphics APIs, like EGL or Vulkan, that manage the buffer queue and
+ commits of a wl_surface themselves, are likely to be using this
+ extension internally. If a client is using such an API for a
+ wl_surface, it should not directly use this extension on that surface,
+ to avoid raising a surface_exists protocol error.
+ </description>
+ <arg name="id" type="new_id" interface="wp_linux_drm_syncobj_surface_v1"
+ summary="the new synchronization surface object id"/>
+ <arg name="surface" type="object" interface="wl_surface"
+ summary="the surface"/>
+ </request>
+
+ <request name="import_timeline">
+ <description summary="import a DRM syncobj timeline">
+ Import a DRM synchronization object timeline.
+
+ If the FD cannot be imported, the invalid_timeline error is raised.
+ </description>
+ <arg name="id" type="new_id" interface="wp_linux_drm_syncobj_timeline_v1"/>
+ <arg name="fd" type="fd" summary="drm_syncobj file descriptor"/>
+ </request>
+ </interface>
+
+ <interface name="wp_linux_drm_syncobj_timeline_v1" version="1">
+ <description summary="synchronization object timeline">
+ This object represents an explicit synchronization object timeline
+ imported by the client to the compositor.
+ </description>
+
+ <request name="destroy" type="destructor">
+ <description summary="destroy the timeline">
+ Destroy the synchronization object timeline. Other objects are not
+ affected by this request, in particular timeline points set by
+ set_acquire_point and set_release_point are not unset.
+ </description>
+ </request>
+ </interface>
+
+ <interface name="wp_linux_drm_syncobj_surface_v1" version="1">
+ <description summary="per-surface explicit synchronization">
+ This object is an add-on interface for wl_surface to enable explicit
+ synchronization.
+
+ Each surface can be associated with only one object of this interface at
+ any time.
+
+ Explicit synchronization is guaranteed to be supported for buffers
+ created with any version of the linux-dmabuf protocol. Compositors are
+ free to support explicit synchronization for additional buffer types.
+ If at surface commit time the attached buffer does not support explicit
+ synchronization, an unsupported_buffer error is raised.
+
+ As long as the wp_linux_drm_syncobj_surface_v1 object is alive, the
+ compositor may ignore implicit synchronization for buffers attached and
+ committed to the wl_surface. The delivery of wl_buffer.release events
+ for buffers attached to the surface becomes undefined.
+
+ Clients must set both acquire and release points if and only if a
+ non-null buffer is attached in the same surface commit. See the
+ no_buffer, no_acquire_point and no_release_point protocol errors.
+
+ If at surface commit time the acquire and release DRM syncobj timelines
+ are identical, the acquire point value must be strictly less than the
+ release point value, or else the conflicting_points protocol error is
+ raised.
+ </description>
+
+ <request name="destroy" type="destructor">
+ <description summary="destroy the surface synchronization object">
+ Destroy this surface synchronization object.
+
+ Any timeline point set by this object with set_acquire_point or
+ set_release_point since the last commit may be discarded by the
+ compositor. Any timeline point set by this object before the last
+ commit will not be affected.
+ </description>
+ </request>
+
+ <enum name="error">
+ <entry name="no_surface" value="1"
+ summary="the associated wl_surface was destroyed"/>
+ <entry name="unsupported_buffer" value="2"
+ summary="the buffer does not support explicit synchronization"/>
+ <entry name="no_buffer" value="3" summary="no buffer was attached"/>
+ <entry name="no_acquire_point" value="4"
+ summary="no acquire timeline point was set"/>
+ <entry name="no_release_point" value="5"
+ summary="no release timeline point was set"/>
+ <entry name="conflicting_points" value="6"
+ summary="acquire and release timeline points are in conflict"/>
+ </enum>
+
+ <request name="set_acquire_point">
+ <description summary="set the acquire timeline point">
+ Set the timeline point that must be signalled before the compositor may
+ sample from the buffer attached with wl_surface.attach.
+
+ The 64-bit unsigned value combined from point_hi and point_lo is the
+ point value.
+
+ The acquire point is double-buffered state, and will be applied on the
+ next wl_surface.commit request for the associated surface. Thus, it
+ applies only to the buffer that is attached to the surface at commit
+ time.
+
+ If an acquire point has already been attached during the same commit
+ cycle, the new point replaces the old one.
+
+ If the associated wl_surface was destroyed, a no_surface error is
+ raised.
+
+ If at surface commit time there is a pending acquire timeline point set
+ but no pending buffer attached, a no_buffer error is raised. If at
+ surface commit time there is a pending buffer attached but no pending
+ acquire timeline point set, the no_acquire_point protocol error is
+ raised.
+ </description>
+ <arg name="timeline" type="object" interface="wp_linux_drm_syncobj_timeline_v1"/>
+ <arg name="point_hi" type="uint" summary="high 32 bits of the point value"/>
+ <arg name="point_lo" type="uint" summary="low 32 bits of the point value"/>
+ </request>
+
+ <request name="set_release_point">
+ <description summary="set the release timeline point">
+ Set the timeline point that must be signalled by the compositor when it
+ has finished its usage of the buffer attached with wl_surface.attach
+ for the relevant commit.
+
+ Once the timeline point is signaled, and assuming the associated buffer
+ is not pending release from other wl_surface.commit requests, no
+ additional explicit or implicit synchronization with the compositor is
+ required to safely re-use the buffer.
+
+ Note that clients cannot rely on the release point being always
+ signaled after the acquire point: compositors may release buffers
+ without ever reading from them. In addition, the compositor may use
+ different presentation paths for different commits, which may have
+ different release behavior. As a result, the compositor may signal the
+ release points in a different order than the client committed them.
+
+ Because signaling a timeline point also signals every previous point,
+ it is generally not safe to use the same timeline object for the
+ release points of multiple buffers. The out-of-order signaling
+ described above may lead to a release point being signaled before the
+ compositor has finished reading. To avoid this, it is strongly
+ recommended that each buffer should use a separate timeline for its
+ release points.
+
+ The 64-bit unsigned value combined from point_hi and point_lo is the
+ point value.
+
+ The release point is double-buffered state, and will be applied on the
+ next wl_surface.commit request for the associated surface. Thus, it
+ applies only to the buffer that is attached to the surface at commit
+ time.
+
+ If a release point has already been attached during the same commit
+ cycle, the new point replaces the old one.
+
+ If the associated wl_surface was destroyed, a no_surface error is
+ raised.
+
+ If at surface commit time there is a pending release timeline point set
+ but no pending buffer attached, a no_buffer error is raised. If at
+ surface commit time there is a pending buffer attached but no pending
+ release timeline point set, the no_release_point protocol error is
+ raised.
+ </description>
+ <arg name="timeline" type="object" interface="wp_linux_drm_syncobj_timeline_v1"/>
+ <arg name="point_hi" type="uint" summary="high 32 bits of the point value"/>
+ <arg name="point_lo" type="uint" summary="low 32 bits of the point value"/>
+ </request>
+ </interface>
+</protocol>