xserver: Branch 'master' - 11 commits

GitLab Mirror gitlab-mirror at kemper.freedesktop.org
Tue Apr 9 06:34:00 UTC 2024


 .gitlab-ci.yml                        |    2 
 .gitlab-ci/cross-prereqs-build.sh     |    2 
 .gitlab-ci/debian-install.sh          |   17 -
 dri3/dri3.c                           |   15 +
 dri3/dri3.h                           |   38 +++
 dri3/dri3_priv.h                      |    3 
 dri3/dri3_request.c                   |  125 ++++++++++
 dri3/dri3_screen.c                    |   20 +
 hw/xfree86/common/xf86Module.h        |    2 
 hw/xwayland/meson.build               |    3 
 hw/xwayland/xwayland-glamor-gbm.c     |  408 +++++++++++++++++++++++++++++++++-
 hw/xwayland/xwayland-glamor.c         |   48 ++++
 hw/xwayland/xwayland-glamor.h         |   15 +
 hw/xwayland/xwayland-present.c        |  299 ++++++++++++++++++++----
 hw/xwayland/xwayland-present.h        |    9 
 hw/xwayland/xwayland-screen.h         |    3 
 hw/xwayland/xwayland-window-buffers.c |  125 ++++++++++
 hw/xwayland/xwayland-window.c         |    4 
 hw/xwayland/xwayland-window.h         |    1 
 include/protocol-versions.h           |    8 
 meson.build                           |    8 
 present/present.c                     |   15 +
 present/present_execute.c             |   50 +++-
 present/present_priv.h                |   35 ++
 present/present_request.c             |  184 ++++++++++++---
 present/present_scmd.c                |   17 +
 present/present_screen.c              |    1 
 present/present_vblank.c              |   48 ++++
 28 files changed, 1393 insertions(+), 112 deletions(-)

New commits:
commit e1f16fb1aca529b6609ab3b136a867104683ba2a
Author: Erik Kurzinger <ekurzinger at nvidia.com>
Date:   Wed Jan 31 11:36:45 2024 -0800

    xwayland: don't scrap pending present requests
    
    When a present request is received, Xwayland will check if there is an
    existing request targeting the same window and msc and scrap the older
    request if so. Alas, this does not interact well the older fence-based
    or newer syncobj-based synchronization features of the Present
    extension.
    
    Since execution of a request may be delayed for an unknown length of
    time while waiting for a fence to be signaled, the target msc computed
    upon receiving a request may not match the actual msc at which the
    request is executed. Therefore, we cannot determine in advance whether a
    more recently received request will make an older request redundant.
    
    This change removes the code to scrap pending present requests.
    
    We must also ensure requests are executed in the correct order even if
    their fences are signaled out of order. To achieve this, whenever
    execution of a request needs to wait for a fence, execution of any
    later-received requests will be blocked until the earlier request is
    ready. The blocked requests will be added to a list tracked in the
    xwl_present_window struct. Once the earlier request's fence is signaled,
    any blocked requests will be re-executed.
    
    Signed-off-by: Erik Kurzinger <ekurzinger at nvidia.com>
    Part-of: <https://gitlab.freedesktop.org/xorg/xserver/-/merge_requests/967>

diff --git a/hw/xwayland/xwayland-present.c b/hw/xwayland/xwayland-present.c
index 959528a3f..04c3033a5 100644
--- a/hw/xwayland/xwayland-present.c
+++ b/hw/xwayland/xwayland-present.c
@@ -81,6 +81,7 @@ xwl_present_window_get_priv(WindowPtr window)
         xorg_list_init(&xwl_present_window->wait_list);
         xorg_list_init(&xwl_present_window->flip_queue);
         xorg_list_init(&xwl_present_window->idle_queue);
+        xorg_list_init(&xwl_present_window->blocked_queue);
 
         dixSetPrivate(&window->devPrivates,
                       &xwl_present_window_private_key,
@@ -172,7 +173,8 @@ xwl_present_has_pending_events(struct xwl_present_window *xwl_present_window)
     present_vblank_ptr flip_pending = xwl_present_get_pending_flip(xwl_present_window);
 
     return (flip_pending && flip_pending->sync_flip) ||
-           !xorg_list_is_empty(&xwl_present_window->wait_list);
+           !xorg_list_is_empty(&xwl_present_window->wait_list) ||
+           !xorg_list_is_empty(&xwl_present_window->blocked_queue);
 }
 
 void
@@ -982,6 +984,34 @@ xwl_present_wait_acquire_fence_avail(struct xwl_screen *xwl_screen,
     return FALSE;
 }
 
+static void
+xwl_present_flush_blocked(struct xwl_present_window *xwl_present_window,
+                          uint64_t crtc_msc)
+{
+    struct xwl_screen *xwl_screen =
+        xwl_screen_get(xwl_present_window->window->drawable.pScreen);
+    struct xwl_present_event *blocked_event, *tmp;
+
+    if (!xwl_present_window->blocking_event)
+        return;
+
+    xwl_present_window->blocking_event = 0;
+
+    xorg_list_for_each_entry_safe(blocked_event, tmp,
+                                  &xwl_present_window->blocked_queue,
+                                  blocked) {
+        present_vblank_ptr blocked_vblank = &blocked_event->vblank;
+        xorg_list_del(&blocked_event->blocked);
+        if (present_execute_wait(blocked_vblank, crtc_msc) ||
+            xwl_present_wait_acquire_fence_avail(xwl_screen, blocked_vblank)) {
+            xwl_present_window->blocking_event = blocked_vblank->event_id;
+            return;
+        }
+
+        xwl_present_re_execute(blocked_vblank);
+    }
+}
+
 /*
  * Once the required MSC has been reached, execute the pending request.
  *
@@ -999,15 +1029,28 @@ xwl_present_execute(present_vblank_ptr vblank, uint64_t ust, uint64_t crtc_msc)
     present_vblank_ptr flip_pending = xwl_present_get_pending_flip(xwl_present_window);
     struct xwl_present_event *event = xwl_present_event_from_vblank(vblank);
     struct xwl_screen *xwl_screen = xwl_screen_get(window->drawable.pScreen);
+    Bool notify_only = !vblank->window || !vblank->pixmap;
 
     xorg_list_del(&vblank->event_queue);
 
+    if (!notify_only && !event->copy_executed &&
+        xwl_present_window->blocking_event &&
+        xwl_present_window->blocking_event != event->vblank.event_id) {
+        /* an earlier request is blocking execution */
+        xorg_list_append(&event->blocked, &xwl_present_window->blocked_queue);
+        return;
+    }
+
 retry:
     if (present_execute_wait(vblank, crtc_msc) ||
-        xwl_present_wait_acquire_fence_avail(xwl_screen, vblank))
+        xwl_present_wait_acquire_fence_avail(xwl_screen, vblank)) {
+        if (!notify_only)
+            /* block execution of subsequent requests until this request is ready */
+            xwl_present_window->blocking_event = event->vblank.event_id;
         return;
+    }
 
-    if (flip_pending && vblank->flip && vblank->pixmap && vblank->window) {
+    if (flip_pending && vblank->flip && !notify_only) {
         DebugPresent(("\tr %" PRIu64 " %p (pending %p)\n",
                       vblank->event_id, vblank, flip_pending));
         xorg_list_append(&vblank->event_queue, &xwl_present_window->flip_queue);
@@ -1017,8 +1060,9 @@ retry:
 
     vblank->queued = FALSE;
 
-    if (vblank->pixmap && vblank->window && !event->copy_executed) {
+    if (!notify_only && !event->copy_executed) {
         ScreenPtr screen = window->drawable.pScreen;
+        int ret;
 
         if (vblank->flip) {
             RegionPtr damage;
@@ -1075,6 +1119,7 @@ retry:
                 /* Realign timer */
                 xwl_present_reset_timer(xwl_present_window);
 
+                xwl_present_flush_blocked(xwl_present_window, crtc_msc);
                 return;
             }
 
@@ -1096,9 +1141,12 @@ retry:
         /* Set the copy_executed field, so this will fall through to present_execute_post next time */
         event->copy_executed = TRUE;
 
-        if (xwl_present_queue_vblank(screen, window, vblank->crtc,
-                                     vblank->event_id, crtc_msc + 1)
-            == Success)
+        ret = xwl_present_queue_vblank(screen, window, vblank->crtc,
+                                       vblank->event_id, crtc_msc + 1);
+
+        xwl_present_flush_blocked(xwl_present_window, crtc_msc);
+
+        if (ret == Success)
             return;
     }
 
@@ -1134,7 +1182,7 @@ xwl_present_pixmap(WindowPtr window,
     uint64_t                    target_msc;
     uint64_t                    crtc_msc = 0;
     int                         ret;
-    present_vblank_ptr          vblank, tmp;
+    present_vblank_ptr          vblank;
     ScreenPtr                   screen = window->drawable.pScreen;
     present_window_priv_ptr     window_priv = present_get_window_priv(window, TRUE);
     present_screen_priv_ptr     screen_priv = present_screen_priv(screen);
@@ -1169,35 +1217,6 @@ xwl_present_pixmap(WindowPtr window,
                                         remainder,
                                         options);
 
-    /*
-     * Look for a matching presentation already on the list...
-     */
-
-    if (!update && pixmap) {
-        xorg_list_for_each_entry_safe(vblank, tmp, &window_priv->vblank, window_list) {
-
-            if (!vblank->pixmap)
-                continue;
-
-            if (!vblank->queued)
-                continue;
-
-            if (vblank->target_msc != target_msc)
-                continue;
-
-            if (xwl_present_event_from_vblank(vblank)->copy_executed)
-                continue;
-
-            if (vblank->release_syncobj)
-                vblank->release_syncobj->signal(vblank->release_syncobj,
-                                                vblank->release_point);
-
-            present_vblank_scrap(vblank);
-            if (vblank->flip_ready)
-                xwl_present_re_execute(vblank);
-        }
-    }
-
     event = calloc(1, sizeof(*event));
     if (!event)
         return BadAlloc;
diff --git a/hw/xwayland/xwayland-present.h b/hw/xwayland/xwayland-present.h
index ced79868f..c0efaa6bf 100644
--- a/hw/xwayland/xwayland-present.h
+++ b/hw/xwayland/xwayland-present.h
@@ -50,8 +50,10 @@ struct xwl_present_window {
     struct xorg_list wait_list;
     struct xorg_list flip_queue;
     struct xorg_list idle_queue;
+    struct xorg_list blocked_queue;
 
     present_vblank_ptr flip_active;
+    uint64_t blocking_event;
 };
 
 struct xwl_present_event {
@@ -63,6 +65,8 @@ struct xwl_present_event {
     uint32_t options;
     uint64_t divisor;
     uint64_t remainder;
+
+    struct xorg_list blocked;
 };
 
 Bool xwl_present_entered_for_each_frame_callback(void);
commit 87bf2cafcc27e609e4a75d0596682c2619f3f1dc
Author: Erik Kurzinger <ekurzinger at nvidia.com>
Date:   Tue Aug 15 15:32:47 2023 -0700

    xwayland: add support for wp_linux_drm_syncobj_v1
    
    This protocol allows for explicit synchronization of GPU operations by
    Wayland clients and the compositor. Xwayland can make use of this to
    ensure any rendering it initiates has completed before the target image
    is accessed by the compositor, without having to rely on kernel-level
    implicit synchronization.
    
    Furthermore, for X11 clients that also support explicit synchronization
    using the mechanisms exposed in the DRI3 and Present extensions, this
    Wayland protocol allows us to simply forward the timeline, acquire, and
    release points directly to the compositor, ideally avoiding any
    premature stalls in the presentation pipeline.
    
    Signed-off-by: Erik Kurzinger <ekurzinger at nvidia.com>
    Part-of: <https://gitlab.freedesktop.org/xorg/xserver/-/merge_requests/967>

diff --git a/hw/xwayland/meson.build b/hw/xwayland/meson.build
index 7a9645c00..0097ca8b2 100644
--- a/hw/xwayland/meson.build
+++ b/hw/xwayland/meson.build
@@ -50,6 +50,7 @@ shortcuts_inhibit_xml = join_paths(protodir, 'unstable', 'keyboard-shortcuts-inh
 xwayland_shell_xml = join_paths(protodir, 'staging', 'xwayland-shell', 'xwayland-shell-v1.xml')
 tearing_xml = join_paths(protodir, 'staging', 'tearing-control', 'tearing-control-v1.xml')
 fractional_scale_xml = join_paths(protodir, 'staging', 'fractional-scale', 'fractional-scale-v1.xml')
+syncobj_xml = join_paths(protodir, 'staging', 'linux-drm-syncobj', 'linux-drm-syncobj-v1.xml')
 
 client_header = generator(scanner,
     output : '@BASENAME at -client-protocol.h',
@@ -80,6 +81,7 @@ srcs += client_header.process(shortcuts_inhibit_xml)
 srcs += client_header.process(xwayland_shell_xml)
 srcs += client_header.process(tearing_xml)
 srcs += client_header.process(fractional_scale_xml)
+srcs += client_header.process(syncobj_xml)
 srcs += code.process(relative_xml)
 srcs += code.process(pointer_xml)
 srcs += code.process(gestures_xml)
@@ -94,6 +96,7 @@ srcs += code.process(shortcuts_inhibit_xml)
 srcs += code.process(xwayland_shell_xml)
 srcs += code.process(tearing_xml)
 srcs += code.process(fractional_scale_xml)
+srcs += code.process(syncobj_xml)
 
 if build_ei
     xwayland_dep += libei_dep
diff --git a/hw/xwayland/xwayland-glamor-gbm.c b/hw/xwayland/xwayland-glamor-gbm.c
index bb37d1556..5bfbb3262 100644
--- a/hw/xwayland/xwayland-glamor-gbm.c
+++ b/hw/xwayland/xwayland-glamor-gbm.c
@@ -55,6 +55,7 @@
 #include "xwayland-screen.h"
 
 #include "linux-dmabuf-unstable-v1-client-protocol.h"
+#include "linux-drm-syncobj-v1-client-protocol.h"
 
 struct xwl_gbm_private {
     drmDevice *device;
@@ -620,6 +621,8 @@ xwl_glamor_gbm_cleanup(struct xwl_screen *xwl_screen)
         wl_drm_destroy(xwl_gbm->drm);
     if (xwl_gbm->gbm)
         gbm_device_destroy(xwl_gbm->gbm);
+    if (xwl_screen->explicit_sync)
+        wp_linux_drm_syncobj_manager_v1_destroy(xwl_screen->explicit_sync);
 
     free(xwl_gbm);
 }
@@ -965,8 +968,38 @@ struct xwl_dri3_syncobj
 {
     struct dri3_syncobj base;
     uint32_t handle;
+    struct wp_linux_drm_syncobj_timeline_v1 *timeline;
 };
 
+void
+xwl_glamor_dri3_syncobj_passthrough(WindowPtr window,
+                                    struct dri3_syncobj *acquire_syncobj,
+                                    struct dri3_syncobj *release_syncobj,
+                                    uint64_t acquire_point,
+                                    uint64_t release_point)
+{
+    struct xwl_window *xwl_window = xwl_window_from_window(window);
+    struct xwl_screen *xwl_screen = xwl_window->xwl_screen;
+    struct xwl_dri3_syncobj *xwl_acquire_syncobj = (struct xwl_dri3_syncobj *)acquire_syncobj;
+    struct xwl_dri3_syncobj *xwl_release_syncobj = (struct xwl_dri3_syncobj *)release_syncobj;
+    uint32_t acquire_hi = acquire_point >> 32;
+    uint32_t acquire_lo = acquire_point & 0xffffffff;
+    uint32_t release_hi = release_point >> 32;
+    uint32_t release_lo = release_point & 0xffffffff;
+
+    if (!xwl_window->surface_sync)
+        xwl_window->surface_sync =
+            wp_linux_drm_syncobj_manager_v1_get_surface(xwl_screen->explicit_sync,
+                                                        xwl_window->surface);
+
+    wp_linux_drm_syncobj_surface_v1_set_acquire_point(xwl_window->surface_sync,
+                                                      xwl_acquire_syncobj->timeline,
+                                                      acquire_hi, acquire_lo);
+    wp_linux_drm_syncobj_surface_v1_set_release_point(xwl_window->surface_sync,
+                                                      xwl_release_syncobj->timeline,
+                                                      release_hi, release_lo);
+}
+
 static Bool
 xwl_dri3_check_syncobj(struct dri3_syncobj *syncobj, uint64_t point, Bool check_avail)
 {
@@ -1046,6 +1079,9 @@ xwl_dri3_free_syncobj(struct dri3_syncobj *syncobj)
     struct xwl_screen *xwl_screen = xwl_screen_get(syncobj->screen);
     struct xwl_gbm_private *xwl_gbm = xwl_gbm_get(xwl_screen);
 
+    if (xwl_syncobj->timeline)
+        wp_linux_drm_syncobj_timeline_v1_destroy(xwl_syncobj->timeline);
+
     if (xwl_syncobj->handle)
         drmSyncobjDestroy(xwl_gbm->drm_fd, xwl_syncobj->handle);
 
@@ -1082,10 +1118,28 @@ static struct dri3_syncobj *
 xwl_dri3_create_syncobj(struct xwl_screen *xwl_screen, uint32_t handle)
 {
     struct xwl_dri3_syncobj *syncobj = calloc(1, sizeof (*syncobj));
+    struct xwl_gbm_private *xwl_gbm = xwl_gbm_get(xwl_screen);
+    Bool create = !handle;
 
     if (!syncobj)
         return NULL;
 
+    if (create && drmSyncobjCreate(xwl_gbm->drm_fd, 0, &handle))
+        goto fail;
+
+    if (xwl_screen->explicit_sync) {
+        int syncobj_fd = -1;
+        if (drmSyncobjHandleToFD(xwl_gbm->drm_fd, handle, &syncobj_fd))
+            goto fail;
+
+        syncobj->timeline =
+            wp_linux_drm_syncobj_manager_v1_import_timeline(xwl_screen->explicit_sync,
+                                                            syncobj_fd);
+        close(syncobj_fd);
+        if (!syncobj->timeline)
+            goto fail;
+    }
+
     syncobj->handle = handle;
     syncobj->base.screen = xwl_screen->screen;
     syncobj->base.refcount = 1;
@@ -1099,6 +1153,18 @@ xwl_dri3_create_syncobj(struct xwl_screen *xwl_screen, uint32_t handle)
     syncobj->base.signaled_eventfd = xwl_dri3_syncobj_signaled_eventfd;
     syncobj->base.submitted_eventfd = xwl_dri3_syncobj_submitted_eventfd;
     return &syncobj->base;
+
+fail:
+    if (create && handle)
+        drmSyncobjDestroy(xwl_gbm->drm_fd, handle);
+    free(syncobj);
+    return NULL;
+}
+
+struct dri3_syncobj *
+xwl_glamor_dri3_syncobj_create(struct xwl_screen *xwl_screen)
+{
+    return xwl_dri3_create_syncobj(xwl_screen, 0 /* allocate new handle */);
 }
 
 static struct dri3_syncobj *
@@ -1301,6 +1367,17 @@ xwl_screen_set_drm_interface(struct xwl_screen *xwl_screen,
     return TRUE;
 }
 
+Bool
+xwl_screen_set_syncobj_interface(struct xwl_screen *xwl_screen,
+                                 uint32_t id, uint32_t version)
+{
+    xwl_screen->explicit_sync =
+        wl_registry_bind(xwl_screen->registry, id,
+                         &wp_linux_drm_syncobj_manager_v1_interface,
+                         version);
+    return TRUE;
+}
+
 static Bool
 xwl_glamor_gbm_has_egl_extension(void)
 {
@@ -1545,6 +1622,12 @@ xwl_glamor_gbm_init_egl(struct xwl_screen *xwl_screen)
                                 "ANDROID_native_fence_sync"))
         xwl_gbm->supports_syncobjs = TRUE;
 
+    if (!xwl_gbm->supports_syncobjs && xwl_screen->explicit_sync) {
+        /* explicit sync requires syncobj support */
+        wp_linux_drm_syncobj_manager_v1_destroy(xwl_screen->explicit_sync);
+        xwl_screen->explicit_sync = NULL;
+    }
+
     return TRUE;
 error:
     if (xwl_screen->egl_display != EGL_NO_DISPLAY) {
diff --git a/hw/xwayland/xwayland-glamor.c b/hw/xwayland/xwayland-glamor.c
index eb15eea2d..f03366437 100644
--- a/hw/xwayland/xwayland-glamor.c
+++ b/hw/xwayland/xwayland-glamor.c
@@ -41,6 +41,7 @@
 
 #include "drm-client-protocol.h"
 #include "linux-dmabuf-unstable-v1-client-protocol.h"
+#include "linux-drm-syncobj-v1-client-protocol.h"
 
 #include "xwayland-dmabuf.h"
 #include "xwayland-glamor.h"
@@ -111,6 +112,8 @@ xwl_glamor_init_wl_registry(struct xwl_screen *xwl_screen,
         xwl_screen_set_drm_interface(xwl_screen, id, version);
     else if (strcmp(interface, zwp_linux_dmabuf_v1_interface.name) == 0)
         xwl_screen_set_dmabuf_interface(xwl_screen, id, version);
+    else if (strcmp(interface, wp_linux_drm_syncobj_manager_v1_interface.name) == 0)
+        xwl_screen_set_syncobj_interface(xwl_screen, id, version);
 }
 
 static Bool
@@ -293,6 +296,27 @@ xwl_glamor_get_fence(struct xwl_screen *xwl_screen)
     return fence_fd;
 }
 
+void
+xwl_glamor_wait_fence(struct xwl_screen *xwl_screen, int fence_fd)
+{
+    EGLint attribs[3];
+    EGLSyncKHR sync;
+
+    if (!xwl_screen->glamor)
+        return;
+
+    xwl_glamor_egl_make_current(xwl_screen);
+
+    attribs[0] = EGL_SYNC_NATIVE_FENCE_FD_ANDROID;
+    attribs[1] = fence_fd;
+    attribs[2] = EGL_NONE;
+    sync = eglCreateSyncKHR(xwl_screen->egl_display, EGL_SYNC_NATIVE_FENCE_ANDROID, attribs);
+    if (sync != EGL_NO_SYNC_KHR) {
+        eglWaitSyncKHR(xwl_screen->egl_display, sync, 0);
+        eglDestroySyncKHR(xwl_screen->egl_display, sync);
+    }
+}
+
 Bool
 xwl_glamor_init(struct xwl_screen *xwl_screen)
 {
diff --git a/hw/xwayland/xwayland-glamor.h b/hw/xwayland/xwayland-glamor.h
index b3fa8bfc3..bbf930437 100644
--- a/hw/xwayland/xwayland-glamor.h
+++ b/hw/xwayland/xwayland-glamor.h
@@ -35,6 +35,7 @@
 
 #include "xwayland-types.h"
 #include "xwayland-glamor-gbm.h"
+#include "dri3.h"
 
 typedef enum _xwl_glamor_mode_flags{
     XWL_GLAMOR_NONE = 0,
@@ -49,6 +50,8 @@ Bool xwl_glamor_init(struct xwl_screen *xwl_screen);
 
 Bool xwl_screen_set_drm_interface(struct xwl_screen *xwl_screen,
                                   uint32_t id, uint32_t version);
+Bool xwl_screen_set_syncobj_interface(struct xwl_screen *xwl_screen,
+                                      uint32_t id, uint32_t version);
 struct wl_buffer *xwl_glamor_pixmap_get_wl_buffer(PixmapPtr pixmap);
 void xwl_glamor_init_wl_registry(struct xwl_screen *xwl_screen,
                                  struct wl_registry *registry,
@@ -62,6 +65,13 @@ void xwl_glamor_dmabuf_import_sync_file(PixmapPtr pixmap, int sync_file);
 int xwl_glamor_dmabuf_export_sync_file(PixmapPtr pixmap);
 Bool xwl_glamor_supports_syncobjs(struct xwl_screen *xwl_screen);
 int xwl_glamor_get_fence(struct xwl_screen *screen);
+void xwl_glamor_wait_fence(struct xwl_screen *xwl_screen, int fence);
+struct dri3_syncobj *xwl_glamor_dri3_syncobj_create(struct xwl_screen *xwl_screen);
+void xwl_glamor_dri3_syncobj_passthrough(WindowPtr window,
+                                         struct dri3_syncobj *acquire_syncobj,
+                                         struct dri3_syncobj *release_syncobj,
+                                         uint64_t acquire_point,
+                                         uint64_t release_point);
 
 #ifdef XV
 /* glamor Xv Adaptor */
diff --git a/hw/xwayland/xwayland-present.c b/hw/xwayland/xwayland-present.c
index 2edfa22c1..959528a3f 100644
--- a/hw/xwayland/xwayland-present.c
+++ b/hw/xwayland/xwayland-present.c
@@ -40,6 +40,7 @@
 #include "xwayland-pixmap.h"
 
 #include "tearing-control-v1-client-protocol.h"
+#include "linux-drm-syncobj-v1-client-protocol.h"
 
 #define XWL_PRESENT_CAPS PresentCapabilityAsync | PresentCapabilityAsyncMayTear
 
@@ -389,6 +390,7 @@ static void
 xwl_present_flip_notify_vblank(present_vblank_ptr vblank, uint64_t ust, uint64_t crtc_msc)
 {
     WindowPtr                   window = vblank->window;
+    struct xwl_screen *xwl_screen = xwl_screen_get(window->drawable.pScreen);
     struct xwl_present_window *xwl_present_window = xwl_present_window_priv(window);
     uint8_t mode = PresentCompleteModeFlip;
 
@@ -405,9 +407,14 @@ xwl_present_flip_notify_vblank(present_vblank_ptr vblank, uint64_t ust, uint64_t
         struct xwl_present_event *event =
             xwl_present_event_from_vblank(xwl_present_window->flip_active);
 
-        if (!event->pixmap)
+        if (!event->pixmap
+#ifdef DRI3
+            /* If this flip used explicit sync, we won't get a release event */
+            || (xwl_screen->explicit_sync && vblank->release_syncobj)
+#endif /* DRI3 */
+            ) {
             xwl_present_free_event(event);
-        else
+        } else
             /* Put the previous flip in the idle_queue and wait for further notice from
              * the Wayland compositor
              */
@@ -767,7 +774,8 @@ xwl_present_check_flip(RRCrtcPtr crtc,
         !xwl_glamor_check_flip(present_window, pixmap))
         return FALSE;
 
-    if (!xwl_glamor_supports_implicit_sync(xwl_window->xwl_screen))
+    if (!xwl_glamor_supports_implicit_sync(xwl_window->xwl_screen) &&
+        !xwl_window->xwl_screen->explicit_sync)
         return FALSE;
 #endif /* XWL_HAS_GLAMOR */
 
@@ -852,6 +860,7 @@ xwl_present_flip(present_vblank_ptr vblank, RegionPtr damage)
     BoxPtr                      damage_box;
     struct wl_buffer            *buffer;
     struct xwl_present_event    *event = xwl_present_event_from_vblank(vblank);
+    Bool                        implicit_sync = TRUE;
 
     if (!xwl_window)
         return FALSE;
@@ -868,7 +877,33 @@ xwl_present_flip(present_vblank_ptr vblank, RegionPtr damage)
 
     event->pixmap = pixmap;
 
-    xwl_pixmap_set_buffer_release_cb(pixmap, xwl_present_buffer_release, event);
+#ifdef XWL_HAS_GLAMOR
+    if (vblank->acquire_syncobj && vblank->release_syncobj) {
+        if (xwl_window->xwl_screen->explicit_sync) {
+            xwl_glamor_dri3_syncobj_passthrough(present_window,
+                                                vblank->acquire_syncobj,
+                                                vblank->release_syncobj,
+                                                vblank->acquire_point,
+                                                vblank->release_point);
+            implicit_sync = FALSE;
+        } else {
+            /* transfer from acquire syncobj to implicit fence */
+            int fence_fd =
+                vblank->acquire_syncobj->export_fence(vblank->acquire_syncobj,
+                                                      vblank->acquire_point);
+            xwl_glamor_dmabuf_import_sync_file(vblank->pixmap, fence_fd);
+        }
+    }
+#endif /* XWL_HAS_GLAMOR */
+
+    if (implicit_sync) {
+        xwl_pixmap_set_buffer_release_cb(pixmap, xwl_present_buffer_release, event);
+
+        if (xwl_window->surface_sync) {
+            wp_linux_drm_syncobj_surface_v1_destroy(xwl_window->surface_sync);
+            xwl_window->surface_sync = NULL;
+        }
+    }
 
     /* We can flip directly to the main surface (full screen window without clips) */
     wl_surface_attach(xwl_window->surface, buffer, 0, 0);
@@ -911,6 +946,42 @@ xwl_present_flip(present_vblank_ptr vblank, RegionPtr damage)
     return TRUE;
 }
 
+#ifdef XWL_HAS_GLAMOR
+static void
+xwl_present_acquire_fence_avail(int fd, int xevents, void *data)
+{
+    present_vblank_ptr vblank = data;
+
+    SetNotifyFd(fd, NULL, 0, NULL);
+    close(fd);
+    vblank->efd = -1;
+
+    xwl_present_re_execute(vblank);
+}
+#endif /* XWL_HAS_GLAMOR */
+
+static Bool
+xwl_present_wait_acquire_fence_avail(struct xwl_screen *xwl_screen,
+                                     present_vblank_ptr vblank)
+{
+#ifdef XWL_HAS_GLAMOR
+    /* If the compositor does not support explicit sync we need to wait for the
+     * acquire fence to be submitted before flipping. */
+    if (vblank->flip && !xwl_screen->explicit_sync &&
+        vblank->pixmap && vblank->acquire_syncobj &&
+        !vblank->acquire_syncobj->has_fence(vblank->acquire_syncobj,
+                                            vblank->acquire_point)) {
+        vblank->efd = eventfd(0, EFD_CLOEXEC);
+        SetNotifyFd(vblank->efd, xwl_present_acquire_fence_avail, X_NOTIFY_READ, vblank);
+        vblank->acquire_syncobj->submitted_eventfd(vblank->acquire_syncobj,
+                                                   vblank->acquire_point,
+                                                   vblank->efd);
+        return TRUE;
+    }
+#endif /* XWL_HAS_GLAMOR */
+    return FALSE;
+}
+
 /*
  * Once the required MSC has been reached, execute the pending request.
  *
@@ -927,11 +998,13 @@ xwl_present_execute(present_vblank_ptr vblank, uint64_t ust, uint64_t crtc_msc)
     struct xwl_present_window *xwl_present_window = xwl_present_window_get_priv(window);
     present_vblank_ptr flip_pending = xwl_present_get_pending_flip(xwl_present_window);
     struct xwl_present_event *event = xwl_present_event_from_vblank(vblank);
+    struct xwl_screen *xwl_screen = xwl_screen_get(window->drawable.pScreen);
 
     xorg_list_del(&vblank->event_queue);
 
 retry:
-    if (present_execute_wait(vblank, crtc_msc))
+    if (present_execute_wait(vblank, crtc_msc) ||
+        xwl_present_wait_acquire_fence_avail(xwl_screen, vblank))
         return;
 
     if (flip_pending && vblank->flip && vblank->pixmap && vblank->window) {
@@ -967,7 +1040,6 @@ retry:
             if (xwl_present_flip(vblank, damage)) {
                 WindowPtr toplvl_window = xwl_present_toplvl_pixmap_window(vblank->window);
                 struct xwl_window *xwl_window = xwl_window_from_window(window);
-                struct xwl_screen *xwl_screen = xwl_window->xwl_screen;
                 PixmapPtr old_pixmap = screen->GetWindowPixmap(window);
 
                 /* Replace window pixmap with flip pixmap */
diff --git a/hw/xwayland/xwayland-screen.h b/hw/xwayland/xwayland-screen.h
index d0b510d1b..2b3bdb29e 100644
--- a/hw/xwayland/xwayland-screen.h
+++ b/hw/xwayland/xwayland-screen.h
@@ -112,6 +112,7 @@ struct xwl_screen {
     struct xwayland_shell_v1 *xwayland_shell;
     struct wp_tearing_control_manager_v1 *tearing_control_manager;
     struct wp_fractional_scale_manager_v1 *fractional_scale_manager;
+    struct wp_linux_drm_syncobj_manager_v1 *explicit_sync;
     struct xorg_list drm_lease_devices;
     struct xorg_list queued_drm_lease_devices;
     struct xorg_list drm_leases;
diff --git a/hw/xwayland/xwayland-window-buffers.c b/hw/xwayland/xwayland-window-buffers.c
index 85c987de7..042a55fc0 100644
--- a/hw/xwayland/xwayland-window-buffers.c
+++ b/hw/xwayland/xwayland-window-buffers.c
@@ -35,6 +35,11 @@
 #ifdef XWL_HAS_GLAMOR
 #include "glamor.h"
 #endif
+#include "dri3.h"
+
+#include <poll.h>
+#include <sys/eventfd.h>
+#include "linux-drm-syncobj-v1-client-protocol.h"
 
 #define BUFFER_TIMEOUT 1 * 1000 /* ms */
 
@@ -42,6 +47,11 @@ struct xwl_window_buffer {
     struct xwl_window *xwl_window;
     PixmapPtr pixmap;
     RegionPtr damage_region;
+#ifdef XWL_HAS_GLAMOR
+    struct dri3_syncobj *syncobj;
+    uint64_t timeline_point;
+    int efd;
+#endif /* XWL_HAS_GLAMOR */
     int refcnt;
     uint32_t time;
     struct xorg_list link_buffer;
@@ -80,6 +90,9 @@ xwl_window_buffer_new(struct xwl_window *xwl_window)
     xwl_window_buffer->damage_region = RegionCreate(NullBox, 1);
     xwl_window_buffer->pixmap = NullPixmap;
     xwl_window_buffer->refcnt = 1;
+#ifdef XWL_HAS_GLAMOR
+    xwl_window_buffer->efd = -1;
+#endif /* XWL_HAS_GLAMOR */
 
     xorg_list_init(&xwl_window_buffer->link_buffer);
 
@@ -109,6 +122,16 @@ xwl_window_buffer_maybe_dispose(struct xwl_window_buffer *xwl_window_buffer)
     if (xwl_window_buffer->pixmap)
         xwl_window_buffer_destroy_pixmap (xwl_window_buffer);
 
+#ifdef XWL_HAS_GLAMOR
+    if (xwl_window_buffer->syncobj)
+        xwl_window_buffer->syncobj->free(xwl_window_buffer->syncobj);
+
+    if (xwl_window_buffer->efd >= 0) {
+        SetNotifyFd(xwl_window_buffer->efd, NULL, 0, NULL);
+        close(xwl_window_buffer->efd);
+    }
+#endif /* XWL_HAS_GLAMOR */
+
     xorg_list_del(&xwl_window_buffer->link_buffer);
     free(xwl_window_buffer);
 
@@ -215,6 +238,20 @@ xwl_window_buffer_release_callback(void *data)
                  xwl_window);
 }
 
+#ifdef XWL_HAS_GLAMOR
+static void
+xwl_window_buffers_release_fence_avail(int fd, int xevents, void *data)
+{
+    struct xwl_window_buffer *xwl_window_buffer = data;
+
+    SetNotifyFd(fd, NULL, 0, NULL);
+    close(fd);
+    xwl_window_buffer->efd = -1;
+
+    xwl_window_buffer_release_callback(data);
+}
+#endif /* XWL_HAS_GLAMOR */
+
 void
 xwl_window_buffers_init(struct xwl_window *xwl_window)
 {
@@ -333,12 +370,53 @@ xwl_window_realloc_pixmap(struct xwl_window *xwl_window)
     screen->DestroyPixmap(window_pixmap);
 }
 
+#ifdef XWL_HAS_GLAMOR
+static Bool
+xwl_window_buffers_set_syncpts(struct xwl_window_buffer *xwl_window_buffer)
+{
+    struct xwl_window *xwl_window = xwl_window_buffer->xwl_window;
+    struct xwl_screen *xwl_screen = xwl_window->xwl_screen;
+    uint64_t acquire_point = ++xwl_window_buffer->timeline_point;
+    uint64_t release_point = ++xwl_window_buffer->timeline_point;
+
+    if (!xwl_window_buffer->syncobj) {
+        struct dri3_syncobj *syncobj = xwl_glamor_dri3_syncobj_create(xwl_screen);
+        if (!syncobj)
+            goto fail;
+        xwl_window_buffer->syncobj = syncobj;
+    }
+
+    int fence_fd = xwl_glamor_get_fence(xwl_screen);
+    if (fence_fd >= 0)
+        xwl_window_buffer->syncobj->import_fence(xwl_window_buffer->syncobj,
+                                                 acquire_point, fence_fd);
+    else
+        goto fail;
+
+    xwl_glamor_dri3_syncobj_passthrough(xwl_window->window,
+                                        xwl_window_buffer->syncobj,
+                                        xwl_window_buffer->syncobj,
+                                        acquire_point,
+                                        release_point);
+    return TRUE;
+
+fail:
+    /* can't use explicit sync, we will do a glFinish() before presenting */
+    if (xwl_window_buffer->syncobj) {
+        xwl_window_buffer->syncobj->free(xwl_window_buffer->syncobj);
+        xwl_window_buffer->syncobj = NULL;
+    }
+    return FALSE;
+}
+#endif /* XWL_HAS_GLAMOR */
+
 PixmapPtr
 xwl_window_swap_pixmap(struct xwl_window *xwl_window)
 {
     struct xwl_screen *xwl_screen = xwl_window->xwl_screen;
     struct xwl_window_buffer *xwl_window_buffer;
     PixmapPtr window_pixmap;
+    Bool implicit_sync = TRUE;
 
     window_pixmap = (*xwl_screen->screen->GetWindowPixmap) (xwl_window->window);
 
@@ -350,6 +428,16 @@ xwl_window_swap_pixmap(struct xwl_window *xwl_window)
         BoxPtr pBox = RegionRects(full_damage);
         int nBox = RegionNumRects(full_damage);
 
+#ifdef XWL_HAS_GLAMOR
+        if (xwl_window_buffer->syncobj) {
+            int fence_fd =
+                xwl_window_buffer->syncobj->export_fence(xwl_window_buffer->syncobj,
+                                                         xwl_window_buffer->timeline_point);
+            xwl_glamor_wait_fence(xwl_screen, fence_fd);
+            close(fence_fd);
+        }
+#endif /* XWL_HAS_GLAMOR */
+
         while (nBox--) {
             copy_pixmap_area(window_pixmap,
                              xwl_window_buffer->pixmap,
@@ -390,12 +478,34 @@ xwl_window_swap_pixmap(struct xwl_window *xwl_window)
 
 #ifdef XWL_HAS_GLAMOR
     if (!xwl_glamor_supports_implicit_sync(xwl_screen)) {
-        glamor_finish(xwl_screen->screen);
+        if (xwl_screen->explicit_sync && xwl_window_buffers_set_syncpts(xwl_window_buffer)) {
+            implicit_sync = FALSE;
+            /* wait until the release fence is available before re-using this buffer */
+            xwl_window_buffer->efd = eventfd(0, EFD_CLOEXEC);
+            SetNotifyFd(xwl_window_buffer->efd, xwl_window_buffers_release_fence_avail,
+                        X_NOTIFY_READ, xwl_window_buffer);
+            xwl_window_buffer->syncobj->submitted_eventfd(xwl_window_buffer->syncobj,
+                                                          xwl_window_buffer->timeline_point,
+                                                          xwl_window_buffer->efd);
+        } else
+            /* If glamor does not support implicit sync and we can't use
+             * explicit sync, wait for the GPU to be idle before presenting.
+             * Note that buffer re-use will still be unsynchronized :(
+             */
+            glamor_finish(xwl_screen->screen);
     }
 #endif /* XWL_HAS_GLAMOR */
-    xwl_pixmap_set_buffer_release_cb(xwl_window_buffer->pixmap,
-                                     xwl_window_buffer_release_callback,
-                                     xwl_window_buffer);
+
+    if (implicit_sync) {
+        xwl_pixmap_set_buffer_release_cb(xwl_window_buffer->pixmap,
+                                         xwl_window_buffer_release_callback,
+                                         xwl_window_buffer);
+
+        if (xwl_window->surface_sync) {
+            wp_linux_drm_syncobj_surface_v1_destroy(xwl_window->surface_sync);
+            xwl_window->surface_sync = NULL;
+        }
+    }
 
     xorg_list_append(&xwl_window_buffer->link_buffer,
                      &xwl_window->window_buffers_unavailable);
diff --git a/hw/xwayland/xwayland-window.c b/hw/xwayland/xwayland-window.c
index f4d8a0227..a8731f4dd 100644
--- a/hw/xwayland/xwayland-window.c
+++ b/hw/xwayland/xwayland-window.c
@@ -55,6 +55,7 @@
 #include "xdg-shell-client-protocol.h"
 #include "xwayland-shell-v1-client-protocol.h"
 #include "fractional-scale-v1-client-protocol.h"
+#include "linux-drm-syncobj-v1-client-protocol.h"
 
 #define DELAYED_WL_SURFACE_DESTROY 1000 /* ms */
 
@@ -1496,6 +1497,9 @@ xwl_unrealize_window(WindowPtr window)
     if (xwl_window->fractional_scale)
         wp_fractional_scale_v1_destroy(xwl_window->fractional_scale);
 
+    if (xwl_window->surface_sync)
+        wp_linux_drm_syncobj_surface_v1_destroy(xwl_window->surface_sync);
+
     release_wl_surface_for_window(xwl_window);
     xorg_list_del(&xwl_window->link_damage);
     xorg_list_del(&xwl_window->link_window);
diff --git a/hw/xwayland/xwayland-window.h b/hw/xwayland/xwayland-window.h
index 108c19e09..a00284fde 100644
--- a/hw/xwayland/xwayland-window.h
+++ b/hw/xwayland/xwayland-window.h
@@ -84,6 +84,7 @@ struct xwl_window {
     struct wp_tearing_control_v1 *tearing_control;
     struct wp_fractional_scale_v1 *fractional_scale;
     int fractional_scale_numerator;
+    struct wp_linux_drm_syncobj_surface_v1 *surface_sync;
 };
 
 struct xwl_window *xwl_window_get(WindowPtr window);
diff --git a/meson.build b/meson.build
index 02ef1ad49..94dea6970 100644
--- a/meson.build
+++ b/meson.build
@@ -64,7 +64,7 @@ libdrm_req = '>= 2.4.116'
 libselinux_req = '>= 2.0.86'
 xext_req = '>= 1.0.99.4'
 wayland_req = '>= 1.21.0'
-wayland_protocols_req = '>= 1.31'
+wayland_protocols_req = '>= 1.34'
 gbm_req = '>= 10.2'
 xf86dgaproto_req = '>= 2.0.99.1'
 xshmfence_req = '>= 1.1'
diff --git a/present/present_execute.c b/present/present_execute.c
index e0396c52e..5253344a1 100644
--- a/present/present_execute.c
+++ b/present/present_execute.c
@@ -79,7 +79,10 @@ present_execute_wait(present_vblank_ptr vblank, uint64_t crtc_msc)
     }
 
 #ifdef DRI3
-    if (vblank->acquire_syncobj &&
+    /* Defer execution of explicitly synchronized copies.
+     * Flip synchronization is managed by the driver.
+     */
+    if (!vblank->flip && vblank->acquire_syncobj &&
         !vblank->acquire_syncobj->is_signaled(vblank->acquire_syncobj,
                                               vblank->acquire_point)) {
         vblank->efd = eventfd(0, EFD_CLOEXEC);
commit 6f85ce4d4eeaa7070b9c2cf4536d35cd14d22090
Author: Erik Kurzinger <ekurzinger at nvidia.com>
Date:   Mon Mar 18 11:36:35 2024 -0700

    xwayland: support DRI3 1.4 and Present 1.4
    
    Together, DRI3 1.4 and Present 1.4 allow clients to explicitly
    synchronize GPU rendering with presentation using DRM syncobjs. Here we
    add the necessary support to Xwayland's glamor and Present
    infrastructure to enable this functionality.
    
    Signed-off-by: Erik Kurzinger <ekurzinger at nvidia.com>
    Part-of: <https://gitlab.freedesktop.org/xorg/xserver/-/merge_requests/967>

diff --git a/hw/xwayland/xwayland-glamor-gbm.c b/hw/xwayland/xwayland-glamor-gbm.c
index 8260b429a..bb37d1556 100644
--- a/hw/xwayland/xwayland-glamor-gbm.c
+++ b/hw/xwayland/xwayland-glamor-gbm.c
@@ -66,6 +66,7 @@ struct xwl_gbm_private {
     Bool dmabuf_capable;
     Bool glamor_gles;
     Bool implicit_sync;
+    Bool supports_syncobjs;
 
     /* Set if wl_drm is available */
     struct wl_drm *drm;
@@ -960,7 +961,192 @@ xwl_glamor_dmabuf_import_sync_file(PixmapPtr pixmap, int sync_file)
     close(sync_file);
 }
 
-static const dri3_screen_info_rec xwl_dri3_info = {
+struct xwl_dri3_syncobj
+{
+    struct dri3_syncobj base;
+    uint32_t handle;
+};
+
+static Bool
+xwl_dri3_check_syncobj(struct dri3_syncobj *syncobj, uint64_t point, Bool check_avail)
+{
+    struct xwl_dri3_syncobj *xwl_syncobj = (struct xwl_dri3_syncobj *)syncobj;
+    struct xwl_screen *xwl_screen = xwl_screen_get(syncobj->screen);
+    struct xwl_gbm_private *xwl_gbm = xwl_gbm_get(xwl_screen);
+
+    return !drmSyncobjTimelineWait(xwl_gbm->drm_fd,
+                                   &xwl_syncobj->handle, &point, 1,
+                                   0 /* timeout */,
+                                   check_avail ?
+                                   DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE :
+                                   DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT,
+                                   NULL /* first_signaled */);
+}
+
+static Bool
+xwl_dri3_syncobj_has_fence(struct dri3_syncobj *syncobj, uint64_t point)
+{
+    return xwl_dri3_check_syncobj(syncobj, point, TRUE /* check_avail */);
+}
+
+static Bool
+xwl_dri3_syncobj_is_signaled(struct dri3_syncobj *syncobj, uint64_t point)
+{
+    return xwl_dri3_check_syncobj(syncobj, point, FALSE /* check_avail */);
+}
+
+static int
+xwl_dri3_syncobj_export_fence(struct dri3_syncobj *syncobj, uint64_t point)
+{
+    struct xwl_dri3_syncobj *xwl_syncobj = (struct xwl_dri3_syncobj *)syncobj;
+    struct xwl_screen *xwl_screen = xwl_screen_get(syncobj->screen);
+    struct xwl_gbm_private *xwl_gbm = xwl_gbm_get(xwl_screen);
+    uint32_t temp_syncobj;
+    int fd = -1;
+
+    drmSyncobjCreate(xwl_gbm->drm_fd, 0, &temp_syncobj);
+    drmSyncobjTransfer(xwl_gbm->drm_fd, temp_syncobj, 0,
+                       xwl_syncobj->handle, point, 0);
+    drmSyncobjExportSyncFile(xwl_gbm->drm_fd, temp_syncobj, &fd);
+    drmSyncobjDestroy(xwl_gbm->drm_fd, temp_syncobj);
+    return fd;
+}
+
+static void
+xwl_dri3_syncobj_import_fence(struct dri3_syncobj *syncobj,
+                              uint64_t point, int fd)
+{
+    struct xwl_dri3_syncobj *xwl_syncobj = (struct xwl_dri3_syncobj *)syncobj;
+    struct xwl_screen *xwl_screen = xwl_screen_get(syncobj->screen);
+    struct xwl_gbm_private *xwl_gbm = xwl_gbm_get(xwl_screen);
+    uint32_t temp_syncobj;
+
+    drmSyncobjCreate(xwl_gbm->drm_fd, 0, &temp_syncobj);
+    drmSyncobjImportSyncFile(xwl_gbm->drm_fd, temp_syncobj, fd);
+    drmSyncobjTransfer(xwl_gbm->drm_fd, xwl_syncobj->handle, point,
+                       temp_syncobj, 0, 0);
+    drmSyncobjDestroy(xwl_gbm->drm_fd, temp_syncobj);
+    close(fd);
+}
+
+static void
+xwl_dri3_signal_syncobj(struct dri3_syncobj *syncobj, uint64_t point)
+{
+    struct xwl_dri3_syncobj *xwl_syncobj = (struct xwl_dri3_syncobj *)syncobj;
+    struct xwl_screen *xwl_screen = xwl_screen_get(syncobj->screen);
+    struct xwl_gbm_private *xwl_gbm = xwl_gbm_get(xwl_screen);
+
+    drmSyncobjTimelineSignal(xwl_gbm->drm_fd, &xwl_syncobj->handle, &point, 1);
+}
+
+static void
+xwl_dri3_free_syncobj(struct dri3_syncobj *syncobj)
+{
+    struct xwl_dri3_syncobj *xwl_syncobj = (struct xwl_dri3_syncobj *)syncobj;
+    struct xwl_screen *xwl_screen = xwl_screen_get(syncobj->screen);
+    struct xwl_gbm_private *xwl_gbm = xwl_gbm_get(xwl_screen);
+
+    if (xwl_syncobj->handle)
+        drmSyncobjDestroy(xwl_gbm->drm_fd, xwl_syncobj->handle);
+
+    free(xwl_syncobj);
+}
+
+static void
+xwl_dri3_syncobj_eventfd(struct dri3_syncobj *syncobj, uint64_t point,
+                         int efd, Bool wait_avail)
+{
+    struct xwl_dri3_syncobj *xwl_syncobj = (struct xwl_dri3_syncobj *)syncobj;
+    struct xwl_screen *xwl_screen = xwl_screen_get(syncobj->screen);
+    struct xwl_gbm_private *xwl_gbm = xwl_gbm_get(xwl_screen);
+
+    drmSyncobjEventfd(xwl_gbm->drm_fd, xwl_syncobj->handle, point, efd,
+                      wait_avail ? DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE : 0);
+}
+
+static void
+xwl_dri3_syncobj_submitted_eventfd(struct dri3_syncobj *syncobj,
+                                   uint64_t point, int efd)
+{
+    xwl_dri3_syncobj_eventfd(syncobj, point, efd, TRUE /* wait_avail */);
+}
+
+static void
+xwl_dri3_syncobj_signaled_eventfd(struct dri3_syncobj *syncobj,
+                                  uint64_t point, int efd)
+{
+    xwl_dri3_syncobj_eventfd(syncobj, point, efd, FALSE /* wait_avail */);
+}
+
+static struct dri3_syncobj *
+xwl_dri3_create_syncobj(struct xwl_screen *xwl_screen, uint32_t handle)
+{
+    struct xwl_dri3_syncobj *syncobj = calloc(1, sizeof (*syncobj));
+
+    if (!syncobj)
+        return NULL;
+
+    syncobj->handle = handle;
+    syncobj->base.screen = xwl_screen->screen;
+    syncobj->base.refcount = 1;
+
+    syncobj->base.free = xwl_dri3_free_syncobj;
+    syncobj->base.has_fence = xwl_dri3_syncobj_has_fence;
+    syncobj->base.is_signaled = xwl_dri3_syncobj_is_signaled;
+    syncobj->base.export_fence = xwl_dri3_syncobj_export_fence;
+    syncobj->base.import_fence = xwl_dri3_syncobj_import_fence;
+    syncobj->base.signal = xwl_dri3_signal_syncobj;
+    syncobj->base.signaled_eventfd = xwl_dri3_syncobj_signaled_eventfd;
+    syncobj->base.submitted_eventfd = xwl_dri3_syncobj_submitted_eventfd;
+    return &syncobj->base;
+}
+
+static struct dri3_syncobj *
+xwl_dri3_import_syncobj(ClientPtr client, ScreenPtr screen, XID id, int fd)
+{
+    struct xwl_screen *xwl_screen = xwl_screen_get(screen);
+    struct xwl_gbm_private *xwl_gbm = xwl_gbm_get(xwl_screen);
+    struct xwl_dri3_syncobj *syncobj = NULL;
+    uint32_t handle;
+
+    if (drmSyncobjFDToHandle(xwl_gbm->drm_fd, fd, &handle))
+        return NULL;
+
+    syncobj = (struct xwl_dri3_syncobj *)xwl_dri3_create_syncobj(xwl_screen, handle);
+    if (!syncobj) {
+        drmSyncobjDestroy(xwl_gbm->drm_fd, handle);
+        return NULL;
+    }
+
+    syncobj->base.id = id;
+
+    return &syncobj->base;
+}
+
+static Bool
+xwl_gbm_supports_syncobjs(struct xwl_screen *xwl_screen)
+{
+    struct xwl_gbm_private *xwl_gbm = xwl_gbm_get(xwl_screen);
+    uint64_t syncobj_cap = 0;
+
+    if (drmGetCap(xwl_gbm->drm_fd, DRM_CAP_SYNCOBJ_TIMELINE,
+                  &syncobj_cap) || !syncobj_cap)
+        return FALSE;
+
+    /* Check if syncobj eventfd is supported. */
+    drmSyncobjEventfd(xwl_gbm->drm_fd, 0, 0, -1, 0);
+    if (errno != ENOENT)
+        return FALSE;
+
+#if !defined(DMA_BUF_IOCTL_EXPORT_SYNC_FILE) || \
+    !defined(DMA_BUF_IOCTL_IMPORT_SYNC_FILE)
+    return FALSE;
+#else
+    return TRUE;
+#endif
+}
+
+static dri3_screen_info_rec xwl_dri3_info = {
     .version = 2,
     .open = NULL,
     .pixmap_from_fds = glamor_pixmap_from_fds,
@@ -969,6 +1155,7 @@ static const dri3_screen_info_rec xwl_dri3_info = {
     .get_formats = xwl_glamor_get_formats,
     .get_modifiers = xwl_glamor_get_modifiers,
     .get_drawable_modifiers = xwl_glamor_get_drawable_modifiers,
+    .import_syncobj = NULL, /* need to check for kernel support */
 };
 
 static const char *
@@ -1129,6 +1316,13 @@ xwl_glamor_supports_implicit_sync(struct xwl_screen *xwl_screen)
         xwl_gbm_get(xwl_screen)->implicit_sync;
 }
 
+Bool
+xwl_glamor_supports_syncobjs(struct xwl_screen *xwl_screen)
+{
+    return xwl_screen->glamor &&
+        xwl_gbm_get(xwl_screen)->supports_syncobjs;
+}
+
 static Bool
 xwl_glamor_try_to_make_context_current(struct xwl_screen *xwl_screen)
 {
@@ -1346,6 +1540,11 @@ xwl_glamor_gbm_init_egl(struct xwl_screen *xwl_screen)
     /* NVIDIA driver does not support implicit sync */
     xwl_gbm->implicit_sync = !strstr(egl_vendor, "NVIDIA");
 
+    if (xwl_gbm_supports_syncobjs(xwl_screen) &&
+        epoxy_has_egl_extension(xwl_screen->egl_display,
+                                "ANDROID_native_fence_sync"))
+        xwl_gbm->supports_syncobjs = TRUE;
+
     return TRUE;
 error:
     if (xwl_screen->egl_display != EGL_NO_DISPLAY) {
@@ -1363,6 +1562,11 @@ xwl_glamor_gbm_init_screen(struct xwl_screen *xwl_screen)
 {
     struct xwl_gbm_private *xwl_gbm = xwl_gbm_get(xwl_screen);
 
+    if (xwl_gbm->supports_syncobjs) {
+        xwl_dri3_info.version = 4;
+        xwl_dri3_info.import_syncobj = xwl_dri3_import_syncobj;
+    }
+
     if (!dri3_screen_init(xwl_screen->screen, &xwl_dri3_info)) {
         ErrorF("Failed to initialize dri3\n");
         goto error;
diff --git a/hw/xwayland/xwayland-glamor.c b/hw/xwayland/xwayland-glamor.c
index d2b7739e1..eb15eea2d 100644
--- a/hw/xwayland/xwayland-glamor.c
+++ b/hw/xwayland/xwayland-glamor.c
@@ -269,6 +269,30 @@ glamor_egl_fd_name_from_pixmap(ScreenPtr screen,
     return 0;
 }
 
+int
+xwl_glamor_get_fence(struct xwl_screen *xwl_screen)
+{
+    EGLint attribs[3];
+    EGLSyncKHR sync;
+    int fence_fd = -1;
+
+    if (!xwl_screen->glamor)
+        return -1;
+
+    xwl_glamor_egl_make_current(xwl_screen);
+
+    attribs[0] = EGL_SYNC_NATIVE_FENCE_FD_ANDROID;
+    attribs[1] = EGL_NO_NATIVE_FENCE_FD_ANDROID;
+    attribs[2] = EGL_NONE;
+    sync = eglCreateSyncKHR(xwl_screen->egl_display, EGL_SYNC_NATIVE_FENCE_ANDROID, attribs);
+    if (sync != EGL_NO_SYNC_KHR) {
+        fence_fd = eglDupNativeFenceFDANDROID(xwl_screen->egl_display, sync);
+        eglDestroySyncKHR(xwl_screen->egl_display, sync);
+    }
+
+    return fence_fd;
+}
+
 Bool
 xwl_glamor_init(struct xwl_screen *xwl_screen)
 {
diff --git a/hw/xwayland/xwayland-glamor.h b/hw/xwayland/xwayland-glamor.h
index 641826a19..b3fa8bfc3 100644
--- a/hw/xwayland/xwayland-glamor.h
+++ b/hw/xwayland/xwayland-glamor.h
@@ -60,6 +60,8 @@ PixmapPtr xwl_glamor_create_pixmap_for_window (struct xwl_window *xwl_window);
 Bool xwl_glamor_supports_implicit_sync(struct xwl_screen *xwl_screen);
 void xwl_glamor_dmabuf_import_sync_file(PixmapPtr pixmap, int sync_file);
 int xwl_glamor_dmabuf_export_sync_file(PixmapPtr pixmap);
+Bool xwl_glamor_supports_syncobjs(struct xwl_screen *xwl_screen);
+int xwl_glamor_get_fence(struct xwl_screen *screen);
 
 #ifdef XV
 /* glamor Xv Adaptor */
diff --git a/hw/xwayland/xwayland-present.c b/hw/xwayland/xwayland-present.c
index ec64d0a95..2edfa22c1 100644
--- a/hw/xwayland/xwayland-present.c
+++ b/hw/xwayland/xwayland-present.c
@@ -30,6 +30,7 @@
 #endif
 #include <windowstr.h>
 #include <present.h>
+#include <sys/eventfd.h>
 
 #include "xwayland-present.h"
 #include "xwayland-screen.h"
@@ -225,7 +226,8 @@ xwl_present_queue_vblank(ScreenPtr screen,
 static uint32_t
 xwl_present_query_capabilities(present_screen_priv_ptr screen_priv)
 {
-    return XWL_PRESENT_CAPS;
+    struct xwl_screen *xwl_screen = xwl_screen_get(screen_priv->pScreen);
+    return xwl_screen->present_capabilities;
 }
 
 static int
@@ -322,7 +324,17 @@ xwl_present_free_event(struct xwl_present_event *event)
 static void
 xwl_present_free_idle_vblank(present_vblank_ptr vblank)
 {
-    present_pixmap_idle(vblank->pixmap, vblank->window, vblank->serial, vblank->idle_fence);
+#ifdef XWL_HAS_GLAMOR
+    if (vblank->release_syncobj) {
+        /* transfer implicit fence to release syncobj */
+        int fence_fd = xwl_glamor_dmabuf_export_sync_file(vblank->pixmap);
+        vblank->release_syncobj->import_fence(vblank->release_syncobj,
+                                              vblank->release_point,
+                                              fence_fd);
+    } else
+#endif /* XWL_HAS_GLAMOR */
+        present_pixmap_idle(vblank->pixmap, vblank->window,
+                            vblank->serial, vblank->idle_fence);
     xwl_present_free_event(xwl_present_event_from_vblank(vblank));
 }
 
@@ -483,7 +495,17 @@ xwl_present_buffer_release(void *data)
         return;
 
     vblank = &event->vblank;
-    present_pixmap_idle(vblank->pixmap, vblank->window, vblank->serial, vblank->idle_fence);
+
+#ifdef XWL_HAS_GLAMOR
+    if (vblank->release_syncobj) {
+        /* transfer implicit fence to release syncobj */
+        int fence_fd = xwl_glamor_dmabuf_export_sync_file(vblank->pixmap);
+        vblank->release_syncobj->import_fence(vblank->release_syncobj,
+                                              vblank->release_point,
+                                              fence_fd);
+    } else
+#endif /* XWL_HAS_GLAMOR */
+        present_pixmap_idle(vblank->pixmap, vblank->window, vblank->serial, vblank->idle_fence);
 
     xwl_present_window = xwl_present_window_priv(vblank->window);
     if (xwl_present_window->flip_active == vblank ||
@@ -679,6 +701,18 @@ xwl_present_maybe_set_reason(struct xwl_window *xwl_window, PresentFlipReason *r
     }
 }
 
+static int
+xwl_present_flush_fenced(WindowPtr window)
+{
+    int fence = -1;
+#ifdef XWL_HAS_GLAMOR
+    struct xwl_screen *xwl_screen = xwl_screen_get(window->drawable.pScreen);
+    fence = xwl_glamor_get_fence(xwl_screen);
+#endif /* XWL_HAS_GLAMOR */
+    xwl_present_flush(window);
+    return fence;
+}
+
 static Bool
 xwl_present_check_flip(RRCrtcPtr crtc,
                        WindowPtr present_window,
@@ -896,6 +930,7 @@ xwl_present_execute(present_vblank_ptr vblank, uint64_t ust, uint64_t crtc_msc)
 
     xorg_list_del(&vblank->event_queue);
 
+retry:
     if (present_execute_wait(vblank, crtc_msc))
         return;
 
@@ -972,6 +1007,8 @@ xwl_present_execute(present_vblank_ptr vblank, uint64_t ust, uint64_t crtc_msc)
             }
 
             vblank->flip = FALSE;
+            /* re-execute, falling through to copy */
+            goto retry;
         }
         DebugPresent(("\tc %p %" PRIu64 ": %08" PRIx32 " -> %08" PRIx32 "\n",
                       vblank, crtc_msc, vblank->pixmap->drawable.id, vblank->window->drawable.id));
@@ -1029,13 +1066,16 @@ xwl_present_pixmap(WindowPtr window,
     ScreenPtr                   screen = window->drawable.pScreen;
     present_window_priv_ptr     window_priv = present_get_window_priv(window, TRUE);
     present_screen_priv_ptr     screen_priv = present_screen_priv(screen);
+    struct xwl_screen          *xwl_screen = xwl_screen_get(screen_priv->pScreen);
+    uint32_t                    caps = xwl_screen->present_capabilities;
     struct xwl_present_event *event;
 
     if (!window_priv)
         return BadAlloc;
 
 #ifdef DRI3
-    if (acquire_syncobj || release_syncobj)
+    if (!(caps & PresentCapabilitySyncobj) &&
+        (acquire_syncobj || release_syncobj))
         return BadValue;
 #endif /* DRI3 */
 
@@ -1076,6 +1116,10 @@ xwl_present_pixmap(WindowPtr window,
             if (xwl_present_event_from_vblank(vblank)->copy_executed)
                 continue;
 
+            if (vblank->release_syncobj)
+                vblank->release_syncobj->signal(vblank->release_syncobj,
+                                                vblank->release_point);
+
             present_vblank_scrap(vblank);
             if (vblank->flip_ready)
                 xwl_present_re_execute(vblank);
@@ -1092,7 +1136,7 @@ xwl_present_pixmap(WindowPtr window,
 #ifdef DRI3
                              acquire_syncobj, release_syncobj, acquire_point, release_point,
 #endif /* DRI3 */
-                             options, XWL_PRESENT_CAPS, notifies, num_notifies, target_msc, crtc_msc)) {
+                             options, caps, notifies, num_notifies, target_msc, crtc_msc)) {
         present_vblank_destroy(vblank);
         return BadAlloc;
     }
@@ -1131,6 +1175,7 @@ xwl_present_unrealize_window(struct xwl_present_window *xwl_present_window)
 Bool
 xwl_present_init(ScreenPtr screen)
 {
+    struct xwl_screen *xwl_screen = xwl_screen_get(screen);
     present_screen_priv_ptr screen_priv;
 
     if (!present_screen_register_priv_keys())
@@ -1146,6 +1191,13 @@ xwl_present_init(ScreenPtr screen)
     if (!dixRegisterPrivateKey(&xwl_present_window_private_key, PRIVATE_WINDOW, 0))
         return FALSE;
 
+    xwl_screen->present_capabilities = XWL_PRESENT_CAPS;
+#ifdef XWL_HAS_GLAMOR
+    if (xwl_glamor_supports_syncobjs(xwl_screen))
+        xwl_screen->present_capabilities |=
+            PresentCapabilitySyncobj;
+#endif /* XWL_HAS_GLAMOR */
+
     screen_priv->query_capabilities = xwl_present_query_capabilities;
     screen_priv->get_crtc = xwl_present_get_crtc;
 
@@ -1156,6 +1208,7 @@ xwl_present_init(ScreenPtr screen)
     screen_priv->present_pixmap = xwl_present_pixmap;
     screen_priv->queue_vblank = xwl_present_queue_vblank;
     screen_priv->flush = xwl_present_flush;
+    screen_priv->flush_fenced = xwl_present_flush_fenced;
     screen_priv->re_execute = xwl_present_re_execute;
 
     screen_priv->abort_vblank = xwl_present_abort_vblank;
diff --git a/hw/xwayland/xwayland-screen.h b/hw/xwayland/xwayland-screen.h
index 477473eea..d0b510d1b 100644
--- a/hw/xwayland/xwayland-screen.h
+++ b/hw/xwayland/xwayland-screen.h
@@ -142,6 +142,8 @@ struct xwl_screen {
     struct libdecor *libdecor_context;
 #endif
     const char *output_name;
+
+    uint32_t present_capabilities;
 };
 
 /* Apps which use randr/vidmode to change the mode when going fullscreen,
diff --git a/meson.build b/meson.build
index 73db72024..02ef1ad49 100644
--- a/meson.build
+++ b/meson.build
@@ -60,7 +60,7 @@ endforeach
 
 add_project_arguments(common_wflags, language : ['c', 'objc'])
 
-libdrm_req = '>= 2.4.109'
+libdrm_req = '>= 2.4.116'
 libselinux_req = '>= 2.0.86'
 xext_req = '>= 1.0.99.4'
 wayland_req = '>= 1.21.0'
commit ac0bc0b3b6e678f6f0157e7ae3a77122783d2eed
Author: Erik Kurzinger <ekurzinger at nvidia.com>
Date:   Mon Mar 18 11:33:15 2024 -0700

    Present: add PresentCapabilitySyncobj and PresentPixmapSynced
    
    Signed-off-by: Erik Kurzinger <ekurzinger at nvidia.com>
    Part-of: <https://gitlab.freedesktop.org/xorg/xserver/-/merge_requests/967>

diff --git a/hw/xfree86/common/xf86Module.h b/hw/xfree86/common/xf86Module.h
index 6166e8559..b6eb80622 100644
--- a/hw/xfree86/common/xf86Module.h
+++ b/hw/xfree86/common/xf86Module.h
@@ -74,7 +74,7 @@
  * mask is 0xFFFF0000.
  */
 #define ABI_ANSIC_VERSION	SET_ABI_VERSION(0, 4)
-#define ABI_VIDEODRV_VERSION	SET_ABI_VERSION(26, 1)
+#define ABI_VIDEODRV_VERSION	SET_ABI_VERSION(27, 0)
 #define ABI_XINPUT_VERSION	SET_ABI_VERSION(24, 4)
 #define ABI_EXTENSION_VERSION	SET_ABI_VERSION(10, 0)
 
diff --git a/hw/xwayland/xwayland-present.c b/hw/xwayland/xwayland-present.c
index eee20c4ce..ec64d0a95 100644
--- a/hw/xwayland/xwayland-present.c
+++ b/hw/xwayland/xwayland-present.c
@@ -1007,6 +1007,12 @@ xwl_present_pixmap(WindowPtr window,
                    RRCrtcPtr target_crtc,
                    SyncFence *wait_fence,
                    SyncFence *idle_fence,
+#ifdef DRI3
+                   struct dri3_syncobj *acquire_syncobj,
+                   struct dri3_syncobj *release_syncobj,
+                   uint64_t acquire_point,
+                   uint64_t release_point,
+#endif /* DRI3 */
                    uint32_t options,
                    uint64_t target_window_msc,
                    uint64_t divisor,
@@ -1028,6 +1034,11 @@ xwl_present_pixmap(WindowPtr window,
     if (!window_priv)
         return BadAlloc;
 
+#ifdef DRI3
+    if (acquire_syncobj || release_syncobj)
+        return BadValue;
+#endif /* DRI3 */
+
     target_crtc = xwl_present_get_crtc(screen_priv, window);
 
     ret = xwl_present_get_ust_msc(screen, window, &ust, &crtc_msc);
@@ -1077,8 +1088,11 @@ xwl_present_pixmap(WindowPtr window,
 
     vblank = &event->vblank;
     if (!present_vblank_init(vblank, window, pixmap, serial, valid, update, x_off, y_off,
-                             target_crtc, wait_fence, idle_fence, options, XWL_PRESENT_CAPS,
-                             notifies, num_notifies, target_msc, crtc_msc)) {
+                             target_crtc, wait_fence, idle_fence,
+#ifdef DRI3
+                             acquire_syncobj, release_syncobj, acquire_point, release_point,
+#endif /* DRI3 */
+                             options, XWL_PRESENT_CAPS, notifies, num_notifies, target_msc, crtc_msc)) {
         present_vblank_destroy(vblank);
         return BadAlloc;
     }
diff --git a/include/protocol-versions.h b/include/protocol-versions.h
index 6d1b4daa5..677b02ab7 100644
--- a/include/protocol-versions.h
+++ b/include/protocol-versions.h
@@ -68,7 +68,11 @@
 
 /* Present */
 #define SERVER_PRESENT_MAJOR_VERSION            1
-#define SERVER_PRESENT_MINOR_VERSION            2
+#ifdef DRI3
+#define SERVER_PRESENT_MINOR_VERSION            4
+#else
+#define SERVER_PRESENT_MINOR_VERSION            3
+#endif
 
 /* RandR */
 #define SERVER_RANDR_MAJOR_VERSION		1
diff --git a/meson.build b/meson.build
index cd52cefaf..73db72024 100644
--- a/meson.build
+++ b/meson.build
@@ -81,7 +81,7 @@ fixesproto_dep = dependency('fixesproto', version: '>= 6.0', fallback: ['xorgpro
 damageproto_dep = dependency('damageproto', version: '>= 1.1', fallback: ['xorgproto', 'ext_xorgproto'])
 xcmiscproto_dep = dependency('xcmiscproto', version: '>= 1.2.0', fallback: ['xorgproto', 'ext_xorgproto'])
 bigreqsproto_dep = dependency('bigreqsproto', version: '>= 1.1.0', fallback: ['xorgproto', 'ext_xorgproto'])
-presentproto_dep = dependency('presentproto', version: '>= 1.3', fallback: ['xorgproto', 'ext_xorgproto'])
+presentproto_dep = dependency('presentproto', version: '>= 1.4', fallback: ['xorgproto', 'ext_xorgproto'])
 xtrans_dep = dependency('xtrans', version: '>= 1.3.5')
 
 videoproto_dep = dependency('videoproto', fallback: ['xorgproto', 'ext_xorgproto'])
diff --git a/present/present.c b/present/present.c
index 211868341..6fb73901e 100644
--- a/present/present.c
+++ b/present/present.c
@@ -230,6 +230,12 @@ present_pixmap(WindowPtr window,
                RRCrtcPtr target_crtc,
                SyncFence *wait_fence,
                SyncFence *idle_fence,
+#ifdef DRI3
+               struct dri3_syncobj *acquire_syncobj,
+               struct dri3_syncobj *release_syncobj,
+               uint64_t acquire_point,
+               uint64_t release_point,
+#endif /* DRI3 */
                uint32_t options,
                uint64_t window_msc,
                uint64_t divisor,
@@ -250,6 +256,12 @@ present_pixmap(WindowPtr window,
                                        target_crtc,
                                        wait_fence,
                                        idle_fence,
+#ifdef DRI3
+                                       acquire_syncobj,
+                                       release_syncobj,
+                                       acquire_point,
+                                       release_point,
+#endif /* DRI3 */
                                        options,
                                        window_msc,
                                        divisor,
@@ -272,6 +284,9 @@ present_notify_msc(WindowPtr window,
                           0, 0,
                           NULL,
                           NULL, NULL,
+#ifdef DRI3
+                          NULL, NULL, 0, 0,
+#endif /* DRI3 */
                           divisor == 0 ? PresentOptionAsync : 0,
                           target_msc, divisor, remainder, NULL, 0);
 }
diff --git a/present/present_execute.c b/present/present_execute.c
index 68a5878be..e0396c52e 100644
--- a/present/present_execute.c
+++ b/present/present_execute.c
@@ -21,6 +21,10 @@
  */
 
 #include "present_priv.h"
+#include <unistd.h>
+#ifdef DRI3
+#include <sys/eventfd.h>
+#endif /* DRI3 */
 
 /*
  * Called when the wait fence is triggered; just gets the current msc/ust and
@@ -37,6 +41,21 @@ present_wait_fence_triggered(void *param)
     screen_priv->re_execute(vblank);
 }
 
+#ifdef DRI3
+static void present_syncobj_triggered(int fd, int xevents, void *data)
+{
+    present_vblank_ptr vblank = data;
+    ScreenPtr screen = vblank->screen;
+    present_screen_priv_ptr screen_priv = present_screen_priv(screen);
+
+    SetNotifyFd(fd, NULL, 0, NULL);
+    close(fd);
+    vblank->efd = -1;
+
+    screen_priv->re_execute(vblank);
+}
+#endif /* DRI3 */
+
 Bool
 present_execute_wait(present_vblank_ptr vblank, uint64_t crtc_msc)
 {
@@ -58,6 +77,20 @@ present_execute_wait(present_vblank_ptr vblank, uint64_t crtc_msc)
             return TRUE;
         }
     }
+
+#ifdef DRI3
+    if (vblank->acquire_syncobj &&
+        !vblank->acquire_syncobj->is_signaled(vblank->acquire_syncobj,
+                                              vblank->acquire_point)) {
+        vblank->efd = eventfd(0, EFD_CLOEXEC);
+        SetNotifyFd(vblank->efd, present_syncobj_triggered, X_NOTIFY_READ, vblank);
+        vblank->acquire_syncobj->signaled_eventfd(vblank->acquire_syncobj,
+                                                  vblank->acquire_point,
+                                                  vblank->efd);
+        return TRUE;
+    }
+#endif /* DRI3 */
+
     return FALSE;
 }
 
@@ -85,9 +118,17 @@ present_execute_copy(present_vblank_ptr vblank, uint64_t crtc_msc)
      * which is then freed, freeing the region
      */
     vblank->update = NULL;
-    screen_priv->flush(window);
-
-    present_pixmap_idle(vblank->pixmap, vblank->window, vblank->serial, vblank->idle_fence);
+#ifdef DRI3
+    if (vblank->release_syncobj) {
+        int fence_fd = screen_priv->flush_fenced(window);
+        vblank->release_syncobj->import_fence(vblank->release_syncobj,
+                                              vblank->release_point, fence_fd);
+    } else
+#endif /* DRI3 */
+    {
+        screen_priv->flush(window);
+        present_pixmap_idle(vblank->pixmap, vblank->window, vblank->serial, vblank->idle_fence);
+    }
 }
 
 void
diff --git a/present/present_priv.h b/present/present_priv.h
index 4ad729864..351f98621 100644
--- a/present/present_priv.h
+++ b/present/present_priv.h
@@ -36,6 +36,7 @@
 #include <xfixes.h>
 #include <randrstr.h>
 #include <inttypes.h>
+#include "dri3.h"
 
 #if 0
 #define DebugPresent(x) ErrorF x
@@ -90,6 +91,13 @@ struct present_vblank {
     Bool                abort_flip;     /* aborting this flip */
     PresentFlipReason   reason;         /* reason for which flip is not possible */
     Bool                has_suboptimal; /* whether client can support SuboptimalCopy mode */
+#ifdef DRI3
+    struct dri3_syncobj *acquire_syncobj;
+    struct dri3_syncobj *release_syncobj;
+    uint64_t            acquire_point;
+    uint64_t            release_point;
+    int                 efd;
+#endif /* DRI3 */
 };
 
 typedef struct present_screen_priv present_screen_priv_rec, *present_screen_priv_ptr;
@@ -124,6 +132,12 @@ typedef int (*present_priv_pixmap_ptr)(WindowPtr window,
                                        RRCrtcPtr target_crtc,
                                        SyncFence *wait_fence,
                                        SyncFence *idle_fence,
+#ifdef DRI3
+                                       struct dri3_syncobj *acquire_syncobj,
+                                       struct dri3_syncobj *release_syncobj,
+                                       uint64_t acquire_point,
+                                       uint64_t release_point,
+#endif /* DRI3 */
                                        uint32_t options,
                                        uint64_t window_msc,
                                        uint64_t divisor,
@@ -137,6 +151,7 @@ typedef int (*present_priv_queue_vblank_ptr)(ScreenPtr screen,
                                              uint64_t event_id,
                                              uint64_t msc);
 typedef void (*present_priv_flush_ptr)(WindowPtr window);
+typedef int (*present_priv_flush_fenced_ptr)(WindowPtr window);
 typedef void (*present_priv_re_execute_ptr)(present_vblank_ptr vblank);
 
 typedef void (*present_priv_abort_vblank_ptr)(ScreenPtr screen,
@@ -147,6 +162,7 @@ typedef void (*present_priv_abort_vblank_ptr)(ScreenPtr screen,
 typedef void (*present_priv_flip_destroy_ptr)(ScreenPtr screen);
 
 struct present_screen_priv {
+    ScreenPtr                   pScreen;
     CloseScreenProcPtr          CloseScreen;
     ConfigNotifyProcPtr         ConfigNotify;
     DestroyWindowProcPtr        DestroyWindow;
@@ -180,6 +196,7 @@ struct present_screen_priv {
 
     present_priv_queue_vblank_ptr       queue_vblank;
     present_priv_flush_ptr              flush;
+    present_priv_flush_fenced_ptr       flush_fenced;
     present_priv_re_execute_ptr         re_execute;
 
     present_priv_abort_vblank_ptr       abort_vblank;
@@ -290,6 +307,12 @@ present_pixmap(WindowPtr window,
                RRCrtcPtr target_crtc,
                SyncFence *wait_fence,
                SyncFence *idle_fence,
+#ifdef DRI3
+               struct dri3_syncobj *acquire_syncobj,
+               struct dri3_syncobj *release_syncobj,
+               uint64_t acquire_point,
+               uint64_t release_point,
+#endif /* DRI3 */
                uint32_t options,
                uint64_t target_msc,
                uint64_t divisor,
@@ -464,6 +487,12 @@ present_vblank_init(present_vblank_ptr vblank,
                     RRCrtcPtr target_crtc,
                     SyncFence *wait_fence,
                     SyncFence *idle_fence,
+#ifdef DRI3
+                    struct dri3_syncobj *acquire_syncobj,
+                    struct dri3_syncobj *release_syncobj,
+                    uint64_t acquire_point,
+                    uint64_t release_point,
+#endif /* DRI3 */
                     uint32_t options,
                     const uint32_t capabilities,
                     present_notify_ptr notifies,
@@ -482,6 +511,12 @@ present_vblank_create(WindowPtr window,
                       RRCrtcPtr target_crtc,
                       SyncFence *wait_fence,
                       SyncFence *idle_fence,
+#ifdef DRI3
+                      struct dri3_syncobj *acquire_syncobj,
+                      struct dri3_syncobj *release_syncobj,
+                      uint64_t acquire_point,
+                      uint64_t release_point,
+#endif /* DRI3 */
                       uint32_t options,
                       const uint32_t capabilities,
                       present_notify_ptr notifies,
diff --git a/present/present_request.c b/present/present_request.c
index f0e8d9909..a9c6ffe66 100644
--- a/present/present_request.c
+++ b/present/present_request.c
@@ -79,79 +79,123 @@ proc_present_query_version(ClientPtr client)
     } while (0)
 
 static int
-proc_present_pixmap(ClientPtr client)
+proc_present_pixmap_common(ClientPtr client,
+                           Window req_window,
+                           Pixmap req_pixmap,
+                           CARD32 req_serial,
+                           CARD32 req_valid,
+                           CARD32 req_update,
+                           INT16 req_x_off,
+                           INT16 req_y_off,
+                           CARD32 req_target_crtc,
+                           XSyncFence req_wait_fence,
+                           XSyncFence req_idle_fence,
+#ifdef DRI3
+                           struct dri3_syncobj *acquire_syncobj,
+                           struct dri3_syncobj *release_syncobj,
+                           CARD64 req_acquire_point,
+                           CARD64 req_release_point,
+#endif /* DRI3 */
+                           CARD32 req_options,
+                           CARD64 req_target_msc,
+                           CARD64 req_divisor,
+                           CARD64 req_remainder,
+                           size_t base_req_size,
+                           xPresentNotify *req_notifies)
 {
-    REQUEST(xPresentPixmapReq);
-    WindowPtr           window;
-    PixmapPtr           pixmap;
-    RegionPtr           valid = NULL;
-    RegionPtr           update = NULL;
-    SyncFence           *wait_fence;
-    SyncFence           *idle_fence;
-    RRCrtcPtr           target_crtc;
-    int                 ret;
-    int                 nnotifies;
-    present_notify_ptr  notifies = NULL;
-
-    REQUEST_AT_LEAST_SIZE(xPresentPixmapReq);
-    ret = dixLookupWindow(&window, stuff->window, client, DixWriteAccess);
+    WindowPtr window;
+    PixmapPtr pixmap;
+    RegionPtr valid = NULL;
+    RegionPtr update = NULL;
+    RRCrtcPtr target_crtc;
+    SyncFence *wait_fence;
+    SyncFence *idle_fence;
+    int nnotifies;
+    present_notify_ptr notifies = NULL;
+    int ret;
+
+    ret = dixLookupWindow(&window, req_window, client, DixWriteAccess);
     if (ret != Success)
         return ret;
-    ret = dixLookupResourceByType((void **) &pixmap, stuff->pixmap, RT_PIXMAP, client, DixReadAccess);
+    ret = dixLookupResourceByType((void **) &pixmap, req_pixmap, RT_PIXMAP, client, DixReadAccess);
     if (ret != Success)
         return ret;
 
     if (window->drawable.depth != pixmap->drawable.depth)
         return BadMatch;
 
-    VERIFY_REGION_OR_NONE(valid, stuff->valid, client, DixReadAccess);
-    VERIFY_REGION_OR_NONE(update, stuff->update, client, DixReadAccess);
+    VERIFY_REGION_OR_NONE(valid, req_valid, client, DixReadAccess);
+    VERIFY_REGION_OR_NONE(update, req_update, client, DixReadAccess);
 
-    VERIFY_CRTC_OR_NONE(target_crtc, stuff->target_crtc, client, DixReadAccess);
+    VERIFY_CRTC_OR_NONE(target_crtc, req_target_crtc, client, DixReadAccess);
 
-    VERIFY_FENCE_OR_NONE(wait_fence, stuff->wait_fence, client, DixReadAccess);
-    VERIFY_FENCE_OR_NONE(idle_fence, stuff->idle_fence, client, DixWriteAccess);
+    VERIFY_FENCE_OR_NONE(wait_fence, req_wait_fence, client, DixReadAccess);
+    VERIFY_FENCE_OR_NONE(idle_fence, req_idle_fence, client, DixWriteAccess);
 
-    if (stuff->options & ~(PresentAllOptions)) {
-        client->errorValue = stuff->options;
+    if (req_options & ~(PresentAllOptions)) {
+        client->errorValue = req_options;
         return BadValue;
     }
 
     /*
      * Check to see if remainder is sane
      */
-    if (stuff->divisor == 0) {
-        if (stuff->remainder != 0) {
-            client->errorValue = (CARD32) stuff->remainder;
+    if (req_divisor == 0) {
+        if (req_remainder != 0) {
+            client->errorValue = (CARD32)req_remainder;
             return BadValue;
         }
     } else {
-        if (stuff->remainder >= stuff->divisor) {
-            client->errorValue = (CARD32) stuff->remainder;
+        if (req_remainder >= req_divisor) {
+            client->errorValue = (CARD32)req_remainder;
             return BadValue;
         }
     }
 
-    nnotifies = (client->req_len << 2) - sizeof (xPresentPixmapReq);
+    nnotifies = (client->req_len << 2) - base_req_size;
     if (nnotifies % sizeof (xPresentNotify))
         return BadLength;
 
     nnotifies /= sizeof (xPresentNotify);
     if (nnotifies) {
-        ret = present_create_notifies(client, nnotifies, (xPresentNotify *) (stuff + 1), &notifies);
+        ret = present_create_notifies(client, nnotifies, req_notifies, &notifies);
         if (ret != Success)
             return ret;
     }
 
-    ret = present_pixmap(window, pixmap, stuff->serial, valid, update,
-                         stuff->x_off, stuff->y_off, target_crtc,
-                         wait_fence, idle_fence, stuff->options,
-                         stuff->target_msc, stuff->divisor, stuff->remainder, notifies, nnotifies);
+    ret = present_pixmap(window, pixmap, req_serial,
+                         valid, update, req_x_off, req_y_off, target_crtc,
+                         wait_fence, idle_fence,
+#ifdef DRI3
+                         acquire_syncobj, release_syncobj,
+                         req_acquire_point, req_release_point,
+#endif /* DRI3 */
+                         req_options, req_target_msc, req_divisor, req_remainder,
+                         notifies, nnotifies);
+
     if (ret != Success)
         present_destroy_notifies(notifies, nnotifies);
     return ret;
 }
 
+static int
+proc_present_pixmap(ClientPtr client)
+{
+    REQUEST(xPresentPixmapReq);
+    REQUEST_AT_LEAST_SIZE(xPresentPixmapReq);
+    return proc_present_pixmap_common(client, stuff->window, stuff->pixmap, stuff->serial,
+                                      stuff->valid, stuff->update, stuff->x_off, stuff->y_off,
+                                      stuff->target_crtc,
+                                      stuff->wait_fence, stuff->idle_fence,
+#ifdef DRI3
+                                      None, None, 0, 0,
+#endif /* DRI3 */
+                                      stuff->options, stuff->target_msc,
+                                      stuff->divisor, stuff->remainder,
+                                      sizeof (xPresentPixmapReq),
+                                      (xPresentNotify *)(stuff + 1));
+}
+
 static int
 proc_present_notify_msc(ClientPtr client)
 {
@@ -240,12 +284,45 @@ proc_present_query_capabilities (ClientPtr client)
     return Success;
 }
 
+#ifdef DRI3
+static int
+proc_present_pixmap_synced (ClientPtr client)
+{
+    REQUEST(xPresentPixmapSyncedReq);
+    struct dri3_syncobj *acquire_syncobj;
+    struct dri3_syncobj *release_syncobj;
+
+    REQUEST_AT_LEAST_SIZE(xPresentPixmapSyncedReq);
+    VERIFY_DRI3_SYNCOBJ(stuff->acquire_syncobj, acquire_syncobj, DixWriteAccess);
+    VERIFY_DRI3_SYNCOBJ(stuff->release_syncobj, release_syncobj, DixWriteAccess);
+
+    if (stuff->acquire_point == 0 || stuff->release_point == 0 ||
+        (stuff->acquire_syncobj == stuff->release_syncobj &&
+         stuff->acquire_point >= stuff->release_point))
+        return BadValue;
+
+    return proc_present_pixmap_common(client, stuff->window, stuff->pixmap, stuff->serial,
+                                      stuff->valid, stuff->update, stuff->x_off, stuff->y_off,
+                                      stuff->target_crtc,
+                                      None, None,
+                                      acquire_syncobj, release_syncobj,
+                                      stuff->acquire_point, stuff->release_point,
+                                      stuff->options, stuff->target_msc,
+                                      stuff->divisor, stuff->remainder,
+                                      sizeof (xPresentPixmapSyncedReq),
+                                      (xPresentNotify *)(stuff + 1));
+}
+#endif /* DRI3 */
+
 static int (*proc_present_vector[PresentNumberRequests]) (ClientPtr) = {
     proc_present_query_version,            /* 0 */
     proc_present_pixmap,                   /* 1 */
     proc_present_notify_msc,               /* 2 */
     proc_present_select_input,             /* 3 */
     proc_present_query_capabilities,       /* 4 */
+#ifdef DRI3
+    proc_present_pixmap_synced,            /* 5 */
+#endif /* DRI3 */
 };
 
 int
@@ -325,12 +402,51 @@ sproc_present_query_capabilities (ClientPtr client)
     return (*proc_present_vector[stuff->presentReqType]) (client);
 }
 
+
+#ifdef DRI3
+static int _X_COLD
+sproc_present_pixmap_synced(ClientPtr client)
+{
+    REQUEST(xPresentPixmapSyncedReq);
+    REQUEST_AT_LEAST_SIZE(xPresentPixmapSyncedReq);
+
+    swaps(&stuff->length);
+
+    swapl(&stuff->window);
+
+    swapl(&stuff->pixmap);
+    swapl(&stuff->serial);
+
+    swapl(&stuff->valid);
+    swapl(&stuff->update);
+
+    swaps(&stuff->x_off);
+    swaps(&stuff->y_off);
+    swapl(&stuff->target_crtc);
+
+    swapl(&stuff->acquire_syncobj);
+    swapl(&stuff->release_syncobj);
+    swapll(&stuff->acquire_point);
+    swapll(&stuff->release_point);
+
+    swapl(&stuff->options);
+
+    swapll(&stuff->target_msc);
+    swapll(&stuff->divisor);
+    swapll(&stuff->remainder);
+    return (*proc_present_vector[stuff->presentReqType]) (client);
+}
+#endif /* DRI3 */
+
 static int (*sproc_present_vector[PresentNumberRequests]) (ClientPtr) = {
     sproc_present_query_version,           /* 0 */
     sproc_present_pixmap,                  /* 1 */
     sproc_present_notify_msc,              /* 2 */
     sproc_present_select_input,            /* 3 */
     sproc_present_query_capabilities,      /* 4 */
+#ifdef DRI3
+    sproc_present_pixmap_synced,           /* 5 */
+#endif /* DRI3 */
 };
 
 int _X_COLD
diff --git a/present/present_scmd.c b/present/present_scmd.c
index d378f0016..ba7d7cb65 100644
--- a/present/present_scmd.c
+++ b/present/present_scmd.c
@@ -738,6 +738,12 @@ present_scmd_pixmap(WindowPtr window,
                     RRCrtcPtr target_crtc,
                     SyncFence *wait_fence,
                     SyncFence *idle_fence,
+#ifdef DRI3
+                    struct dri3_syncobj *acquire_syncobj,
+                    struct dri3_syncobj *release_syncobj,
+                    uint64_t acquire_point,
+                    uint64_t release_point,
+#endif /* DRI3 */
                     uint32_t options,
                     uint64_t target_window_msc,
                     uint64_t divisor,
@@ -754,6 +760,11 @@ present_scmd_pixmap(WindowPtr window,
     present_window_priv_ptr     window_priv = present_get_window_priv(window, TRUE);
     present_screen_priv_ptr     screen_priv = present_screen_priv(screen);
 
+#ifdef DRI3
+    if (acquire_syncobj || release_syncobj)
+        return BadValue;
+#endif /* DRI3 */
+
     if (!window_priv)
         return BadAlloc;
 
@@ -824,6 +835,12 @@ present_scmd_pixmap(WindowPtr window,
                                    target_crtc,
                                    wait_fence,
                                    idle_fence,
+#ifdef DRI3
+                                   acquire_syncobj,
+                                   release_syncobj,
+                                   acquire_point,
+                                   release_point,
+#endif /* DRI3 */
                                    options,
                                    screen_priv->info ? screen_priv->info->capabilities : 0,
                                    notifies,
diff --git a/present/present_screen.c b/present/present_screen.c
index ef56ff779..0589b2ee9 100644
--- a/present/present_screen.c
+++ b/present/present_screen.c
@@ -187,6 +187,7 @@ present_screen_priv_init(ScreenPtr screen)
     wrap(screen_priv, screen, ClipNotify, present_clip_notify);
 
     dixSetPrivate(&screen->devPrivates, &present_screen_private_key, screen_priv);
+    screen_priv->pScreen = screen;
 
     return screen_priv;
 }
diff --git a/present/present_vblank.c b/present/present_vblank.c
index 4f94f16e4..dd255cc60 100644
--- a/present/present_vblank.c
+++ b/present/present_vblank.c
@@ -21,6 +21,7 @@
  */
 
 #include "present_priv.h"
+#include <unistd.h>
 
 void
 present_vblank_notify(present_vblank_ptr vblank, CARD8 kind, CARD8 mode, uint64_t ust, uint64_t crtc_msc)
@@ -55,6 +56,12 @@ present_vblank_init(present_vblank_ptr vblank,
                     RRCrtcPtr target_crtc,
                     SyncFence *wait_fence,
                     SyncFence *idle_fence,
+#ifdef DRI3
+                    struct dri3_syncobj *acquire_syncobj,
+                    struct dri3_syncobj *release_syncobj,
+                    uint64_t acquire_point,
+                    uint64_t release_point,
+#endif /* DRI3 */
                     uint32_t options,
                     const uint32_t capabilities,
                     present_notify_ptr notifies,
@@ -135,6 +142,22 @@ present_vblank_init(present_vblank_ptr vblank,
             goto no_mem;
     }
 
+#ifdef DRI3
+    vblank->efd = -1;
+
+    if (acquire_syncobj) {
+        vblank->acquire_syncobj = acquire_syncobj;
+        ++acquire_syncobj->refcount;
+        vblank->acquire_point = acquire_point;
+    }
+
+    if (release_syncobj) {
+        vblank->release_syncobj = release_syncobj;
+        ++release_syncobj->refcount;
+        vblank->release_point = release_point;
+    }
+#endif /* DRI3 */
+
     if (pixmap)
         DebugPresent(("q %" PRIu64 " %p %" PRIu64 ": %08" PRIx32 " -> %08" PRIx32 " (crtc %p) flip %d vsync %d serial %d\n",
                       vblank->event_id, vblank, target_msc,
@@ -158,6 +181,12 @@ present_vblank_create(WindowPtr window,
                       RRCrtcPtr target_crtc,
                       SyncFence *wait_fence,
                       SyncFence *idle_fence,
+#ifdef DRI3
+                      struct dri3_syncobj *acquire_syncobj,
+                      struct dri3_syncobj *release_syncobj,
+                      uint64_t acquire_point,
+                      uint64_t release_point,
+#endif /* DRI3 */
                       uint32_t options,
                       const uint32_t capabilities,
                       present_notify_ptr notifies,
@@ -172,6 +201,10 @@ present_vblank_create(WindowPtr window,
 
     if (present_vblank_init(vblank, window, pixmap, serial, valid, update,
                             x_off, y_off, target_crtc, wait_fence, idle_fence,
+#ifdef DRI3
+                            acquire_syncobj, release_syncobj,
+                            acquire_point, release_point,
+#endif /* DRI3 */
                             options, capabilities, notifies, num_notifies,
                             target_msc, crtc_msc))
         return vblank;
@@ -229,5 +262,20 @@ present_vblank_destroy(present_vblank_ptr vblank)
     if (vblank->notifies)
         present_destroy_notifies(vblank->notifies, vblank->num_notifies);
 
+#ifdef DRI3
+    if (vblank->efd >= 0) {
+        SetNotifyFd(vblank->efd, NULL, 0, NULL);
+        close(vblank->efd);
+    }
+
+    if (vblank->acquire_syncobj &&
+        --vblank->acquire_syncobj->refcount == 0)
+        vblank->acquire_syncobj->free(vblank->acquire_syncobj);
+
+    if (vblank->release_syncobj &&
+        --vblank->release_syncobj->refcount == 0)
+        vblank->release_syncobj->free(vblank->release_syncobj);
+#endif /* DRI3 */
+
     free(vblank);
 }
commit 0a7b09a041900069aa496ee02a29822203fbcbb2
Author: Erik Kurzinger <ekurzinger at nvidia.com>
Date:   Sat Mar 30 07:42:53 2024 -0700

    xwayland: re-compute target msc during xwl_present_re_execute
    
    If a presentation request is delayed while waiting for a fence, the
    original target msc may no longer be correct. Instead, we should compute
    a new target msc in xwl_present_re_execute.
    
    Signed-off-by: Erik Kurzinger <ekurzinger at nvidia.com>
    Part-of: <https://gitlab.freedesktop.org/xorg/xserver/-/merge_requests/967>

diff --git a/hw/xwayland/xwayland-present.c b/hw/xwayland/xwayland-present.c
index 91dc396fb..eee20c4ce 100644
--- a/hw/xwayland/xwayland-present.c
+++ b/hw/xwayland/xwayland-present.c
@@ -215,6 +215,13 @@ xwl_present_reset_timer(struct xwl_present_window *xwl_present_window)
 static void
 xwl_present_execute(present_vblank_ptr vblank, uint64_t ust, uint64_t crtc_msc);
 
+static int
+xwl_present_queue_vblank(ScreenPtr screen,
+                         WindowPtr present_window,
+                         RRCrtcPtr crtc,
+                         uint64_t event_id,
+                         uint64_t msc);
+
 static uint32_t
 xwl_present_query_capabilities(present_screen_priv_ptr screen_priv)
 {
@@ -237,6 +244,18 @@ xwl_present_get_ust_msc(ScreenPtr screen,
     return Success;
 }
 
+static uint64_t
+xwl_present_get_exec_msc(uint32_t options, uint64_t target_msc)
+{
+    /* Synchronous Xwayland presentations always complete (at least) one frame after they
+     * are executed
+     */
+    if (options & PresentOptionAsyncMayTear)
+        return target_msc;
+
+    return target_msc - 1;
+}
+
 /*
  * When the wait fence or previous flip is completed, it's time
  * to re-try the request
@@ -244,9 +263,27 @@ xwl_present_get_ust_msc(ScreenPtr screen,
 static void
 xwl_present_re_execute(present_vblank_ptr vblank)
 {
+    struct xwl_present_event *event = xwl_present_event_from_vblank(vblank);
     uint64_t ust = 0, crtc_msc = 0;
 
     (void) xwl_present_get_ust_msc(vblank->screen, vblank->window, &ust, &crtc_msc);
+    /* re-compute target / exec msc */
+    vblank->target_msc = present_get_target_msc(0, crtc_msc,
+                                                event->divisor,
+                                                event->remainder,
+                                                event->options);
+    vblank->exec_msc = xwl_present_get_exec_msc(event->options,
+                                                vblank->target_msc);
+
+    vblank->queued = TRUE;
+    if (msc_is_after(vblank->exec_msc, crtc_msc) &&
+        xwl_present_queue_vblank(vblank->screen, vblank->window,
+                                 vblank->crtc,
+                                 vblank->event_id,
+                                 vblank->exec_msc) == Success) {
+        return;
+    }
+
     xwl_present_execute(vblank, ust, crtc_msc);
 }
 
@@ -818,7 +855,7 @@ xwl_present_flip(present_vblank_ptr vblank, RegionPtr damage)
 
     if (xwl_window->tearing_control) {
         uint32_t hint;
-        if (event->async_may_tear)
+        if (event->options & PresentOptionAsyncMayTear)
             hint = WP_TEARING_CONTROL_V1_PRESENTATION_HINT_ASYNC;
         else
             hint = WP_TEARING_CONTROL_V1_PRESENTATION_HINT_VSYNC;
@@ -1047,15 +1084,10 @@ xwl_present_pixmap(WindowPtr window,
     }
 
     vblank->event_id = ++xwl_present_event_id;
-    event->async_may_tear = options & PresentOptionAsyncMayTear;
-
-    /* Synchronous Xwayland presentations always complete (at least) one frame after they
-     * are executed
-     */
-    if (event->async_may_tear)
-        vblank->exec_msc = vblank->target_msc;
-    else
-        vblank->exec_msc = vblank->target_msc - 1;
+    event->options = options;
+    event->divisor = divisor;
+    event->remainder = remainder;
+    vblank->exec_msc = xwl_present_get_exec_msc(options, vblank->target_msc);
 
     vblank->queued = TRUE;
     if (crtc_msc < vblank->exec_msc) {
diff --git a/hw/xwayland/xwayland-present.h b/hw/xwayland/xwayland-present.h
index 34ec0422f..ced79868f 100644
--- a/hw/xwayland/xwayland-present.h
+++ b/hw/xwayland/xwayland-present.h
@@ -58,8 +58,11 @@ struct xwl_present_event {
     present_vblank_rec vblank;
 
     PixmapPtr pixmap;
-    Bool async_may_tear;
     Bool copy_executed;
+
+    uint32_t options;
+    uint64_t divisor;
+    uint64_t remainder;
 };
 
 Bool xwl_present_entered_for_each_frame_callback(void);
commit 3df236a3d538abd758a878bdb7e56dce5b1768a1
Author: Erik Kurzinger <ekurzinger at nvidia.com>
Date:   Fri Mar 29 16:53:56 2024 -0700

    xwayland: add functions to import and export dma-buf implicit fences
    
    Signed-off-by: Erik Kurzinger <ekurzinger at nvidia.com>
    Part-of: <https://gitlab.freedesktop.org/xorg/xserver/-/merge_requests/967>

diff --git a/hw/xwayland/xwayland-glamor-gbm.c b/hw/xwayland/xwayland-glamor-gbm.c
index b22e89248..8260b429a 100644
--- a/hw/xwayland/xwayland-glamor-gbm.c
+++ b/hw/xwayland/xwayland-glamor-gbm.c
@@ -35,6 +35,9 @@
 #include <sys/stat.h>
 #include <xf86drm.h>
 #include <drm_fourcc.h>
+#include <linux/dma-buf.h>
+#include <linux/sync_file.h>
+#include <sys/ioctl.h>
 
 #define MESA_EGL_NO_X11_HEADERS
 #define EGL_NO_X11
@@ -860,6 +863,103 @@ glamor_egl_fd_from_pixmap(ScreenPtr screen, PixmapPtr pixmap,
     return -1;
 }
 
+int
+xwl_glamor_dmabuf_export_sync_file(PixmapPtr pixmap)
+{
+    struct xwl_screen *xwl_screen = xwl_screen_get(pixmap->drawable.pScreen);
+    struct xwl_pixmap *xwl_pixmap;
+    int num_planes;
+    int sync_file = -1;
+    int p;
+#ifndef GBM_BO_FD_FOR_PLANE
+    int32_t first_handle;
+#endif
+
+    if (!xwl_screen->glamor)
+        return -1;
+
+#ifdef DMA_BUF_IOCTL_EXPORT_SYNC_FILE
+    xwl_pixmap = xwl_pixmap_get(pixmap);
+    num_planes = gbm_bo_get_plane_count(xwl_pixmap->bo);
+
+    for (p = 0; p < num_planes; ++p) {
+        int plane_fd;
+#ifdef GBM_BO_FD_FOR_PLANE
+        plane_fd = gbm_bo_get_fd_for_plane(xwl_pixmap->bo, p);
+#else
+        union gbm_bo_handle plane_handle =
+            gbm_bo_get_handle_for_plane(xwl_pixmap->bo, p);
+        if (p == 0)
+            first_handle = plane_handle.s32;
+
+        if (plane_handle.s32 == first_handle)
+            plane_fd = gbm_bo_get_fd(xwl_pixmap->bo);
+        else
+            continue;
+#endif /* GBM_BO_FD_FOR_PLANE */
+        struct dma_buf_export_sync_file export_args = { 0 };
+        export_args.fd = -1;
+        export_args.flags = DMA_BUF_SYNC_READ;
+        drmIoctl(plane_fd, DMA_BUF_IOCTL_EXPORT_SYNC_FILE, &export_args);
+        close(plane_fd);
+        if (sync_file == -1) {
+            sync_file = export_args.fd;
+        } else {
+            struct sync_merge_data merge_args = { 0 };
+            merge_args.fd2 = export_args.fd;
+            ioctl(sync_file, SYNC_IOC_MERGE, &merge_args);
+            close(export_args.fd);
+            close(sync_file);
+            sync_file = merge_args.fence;
+        }
+    }
+#endif /* DMA_BUF_IOCTL_EXPORT_SYNC_FILE */
+    return sync_file;
+}
+
+void
+xwl_glamor_dmabuf_import_sync_file(PixmapPtr pixmap, int sync_file)
+{
+    struct xwl_screen *xwl_screen = xwl_screen_get(pixmap->drawable.pScreen);
+    struct xwl_pixmap *xwl_pixmap;
+    int num_planes;
+    int p;
+#ifndef GBM_BO_FD_FOR_PLANE
+    int32_t first_handle;
+#endif
+
+    if (!xwl_screen->glamor)
+        return;
+
+#ifdef DMA_BUF_IOCTL_IMPORT_SYNC_FILE
+    xwl_pixmap = xwl_pixmap_get(pixmap);
+    num_planes = gbm_bo_get_plane_count(xwl_pixmap->bo);
+
+    for (p = 0; p < num_planes; ++p) {
+        int plane_fd;
+#ifdef GBM_BO_FD_FOR_PLANE
+        plane_fd = gbm_bo_get_fd_for_plane(xwl_pixmap->bo, p);
+#else
+        union gbm_bo_handle plane_handle =
+            gbm_bo_get_handle_for_plane(xwl_pixmap->bo, p);
+        if (p == 0)
+            first_handle = plane_handle.s32;
+
+        if (plane_handle.s32 == first_handle)
+            plane_fd = gbm_bo_get_fd(xwl_pixmap->bo);
+        else
+            continue;
+#endif /* GBM_BO_FD_FOR_PLANE */
+        struct dma_buf_import_sync_file import_args = { 0 };
+        import_args.fd = sync_file;
+        import_args.flags = DMA_BUF_SYNC_READ;
+        drmIoctl(plane_fd, DMA_BUF_IOCTL_IMPORT_SYNC_FILE, &import_args);
+        close(plane_fd);
+    }
+#endif /* DMA_BUF_IOCTL_IMPORT_SYNC_FILE */
+    close(sync_file);
+}
+
 static const dri3_screen_info_rec xwl_dri3_info = {
     .version = 2,
     .open = NULL,
diff --git a/hw/xwayland/xwayland-glamor.h b/hw/xwayland/xwayland-glamor.h
index ff019feda..641826a19 100644
--- a/hw/xwayland/xwayland-glamor.h
+++ b/hw/xwayland/xwayland-glamor.h
@@ -58,6 +58,8 @@ void xwl_glamor_egl_make_current(struct xwl_screen *xwl_screen);
 Bool xwl_glamor_check_flip(WindowPtr present_window, PixmapPtr pixmap);
 PixmapPtr xwl_glamor_create_pixmap_for_window (struct xwl_window *xwl_window);
 Bool xwl_glamor_supports_implicit_sync(struct xwl_screen *xwl_screen);
+void xwl_glamor_dmabuf_import_sync_file(PixmapPtr pixmap, int sync_file);
+int xwl_glamor_dmabuf_export_sync_file(PixmapPtr pixmap);
 
 #ifdef XV
 /* glamor Xv Adaptor */
commit 613dcb6a77d831cd0a6d1e19671b548491ada934
Author: Erik Kurzinger <ekurzinger at nvidia.com>
Date:   Tue Aug 16 11:57:40 2022 -0700

    DRI3: add DRI3ImportSyncobj and DRI3FreeSyncobj
    
    Adds the required infrastructure in the core DRI3 code to support
    importing DRM synchronization objects from clients. This includes
    support for the two new protocol requests from DRI3 version 1.4, an
    internal representation of these objects in the form of the dri3_syncobj
    structure, and an import_syncobj screen info callback.
    
    The following operations are defined for dri3_syncobj objects
    * free - release any server-side resources associated with the object
    * has_fence - check if the fence for a timeline point is submitted
    * is_signaled - check if a timeline point is signaled
    * export_fence - return a sync fd corresponding to a timeline point
    * import_fence - submit a sync fd as the fence for a timeline point
    * signal - immediately signal a timeline point
    * submitted_eventfd and signaled_eventfd - register an eventfd to be
      signaled when the given timeline point is either submitted or
      signaled
    
    Implementations will be responsible for populating these function
    pointers when importing a syncobj.
    
    Signed-off-by: Erik Kurzinger <ekurzinger at nvidia.com>
    Part-of: <https://gitlab.freedesktop.org/xorg/xserver/-/merge_requests/967>

diff --git a/dri3/dri3.c b/dri3/dri3.c
index 191252969..f9c517277 100644
--- a/dri3/dri3.c
+++ b/dri3/dri3.c
@@ -63,6 +63,16 @@ dri3_screen_init(ScreenPtr screen, const dri3_screen_info_rec *info)
     return TRUE;
 }
 
+RESTYPE dri3_syncobj_type;
+
+static int dri3_syncobj_free(void *data, XID id)
+{
+    struct dri3_syncobj *syncobj = data;
+    if (--syncobj->refcount == 0)
+        syncobj->free(syncobj);
+    return 0;
+}
+
 void
 dri3_extension_init(void)
 {
@@ -92,6 +102,11 @@ dri3_extension_init(void)
         if (!dri3_screen_init(screenInfo.screens[i], NULL))
             goto bail;
     }
+
+    dri3_syncobj_type = CreateNewResourceType(dri3_syncobj_free, "DRI3Syncobj");
+    if (!dri3_syncobj_type)
+        goto bail;
+
     return;
 
 bail:
diff --git a/dri3/dri3.h b/dri3/dri3.h
index 02d3b03ee..14f9a1efa 100644
--- a/dri3/dri3.h
+++ b/dri3/dri3.h
@@ -28,7 +28,35 @@
 #include <X11/extensions/dri3proto.h>
 #include <randrstr.h>
 
-#define DRI3_SCREEN_INFO_VERSION        2
+#define DRI3_SCREEN_INFO_VERSION        4
+
+extern RESTYPE dri3_syncobj_type;
+
+struct dri3_syncobj
+{
+    XID id;
+    ScreenPtr screen;
+    uint32_t refcount;
+
+    void (*free)(struct dri3_syncobj *syncobj);
+    Bool (*has_fence)(struct dri3_syncobj *syncobj, uint64_t point);
+    Bool (*is_signaled)(struct dri3_syncobj *syncobj, uint64_t point);
+    int (*export_fence)(struct dri3_syncobj *syncobj, uint64_t point);
+    void (*import_fence)(struct dri3_syncobj *syncobj, uint64_t point, int fd);
+    void (*signal)(struct dri3_syncobj *syncobj, uint64_t point);
+    void (*submitted_eventfd)(struct dri3_syncobj *syncobj, uint64_t point, int efd);
+    void (*signaled_eventfd)(struct dri3_syncobj *syncobj, uint64_t point, int efd);
+};
+
+#define VERIFY_DRI3_SYNCOBJ(id, ptr, a)\
+    do {\
+        int rc = dixLookupResourceByType((void **)&(ptr), id,\
+                                         dri3_syncobj_type, client, a);\
+        if (rc != Success) {\
+            client->errorValue = id;\
+            return rc;\
+        }\
+    } while (0);
 
 typedef int (*dri3_open_proc)(ScreenPtr screen,
                               RRProviderPtr provider,
@@ -84,6 +112,11 @@ typedef int (*dri3_get_drawable_modifiers_proc) (DrawablePtr draw,
                                                  uint32_t *num_modifiers,
                                                  uint64_t **modifiers);
 
+typedef struct dri3_syncobj *(*dri3_import_syncobj_proc) (ClientPtr client,
+                                                          ScreenPtr screen,
+                                                          XID id,
+                                                          int fd);
+
 typedef struct dri3_screen_info {
     uint32_t                    version;
 
@@ -101,6 +134,9 @@ typedef struct dri3_screen_info {
     dri3_get_modifiers_proc     get_modifiers;
     dri3_get_drawable_modifiers_proc get_drawable_modifiers;
 
+    /* Version 4 */
+    dri3_import_syncobj_proc    import_syncobj;
+
 } dri3_screen_info_rec, *dri3_screen_info_ptr;
 
 extern _X_EXPORT Bool
diff --git a/dri3/dri3_priv.h b/dri3/dri3_priv.h
index f319d1770..71d2da957 100644
--- a/dri3/dri3_priv.h
+++ b/dri3/dri3_priv.h
@@ -102,4 +102,7 @@ dri3_get_supported_modifiers(ScreenPtr screen, DrawablePtr drawable,
                              CARD32 *num_screen_modifiers,
                              CARD64 **screen_modifiers);
 
+int
+dri3_import_syncobj(ClientPtr client, ScreenPtr screen, XID id, int fd);
+
 #endif /* _DRI3PRIV_H_ */
diff --git a/dri3/dri3_request.c b/dri3/dri3_request.c
index a5cc7ccc9..98c37aa91 100644
--- a/dri3/dri3_request.c
+++ b/dri3/dri3_request.c
@@ -29,6 +29,17 @@
 #include <drm_fourcc.h>
 #include "randrstr_priv.h"
 
+static Bool
+dri3_screen_can_one_point_four(ScreenPtr screen)
+{
+    dri3_screen_priv_ptr dri3 = dri3_screen_priv(screen);
+
+    return dri3 &&
+        dri3->info &&
+        dri3->info->version >= 4 &&
+        dri3->info->import_syncobj;
+}
+
 static Bool
 dri3_screen_can_one_point_two(ScreenPtr screen)
 {
@@ -62,6 +73,10 @@ proc_dri3_query_version(ClientPtr client)
             rep.minorVersion = 0;
             break;
         }
+        if (!dri3_screen_can_one_point_four(screenInfo.screens[i])) {
+            rep.minorVersion = 2;
+            break;
+        }
     }
 
     for (int i = 0; i < screenInfo.numGPUScreens; i++) {
@@ -69,6 +84,10 @@ proc_dri3_query_version(ClientPtr client)
             rep.minorVersion = 0;
             break;
         }
+        if (!dri3_screen_can_one_point_four(screenInfo.gpuscreens[i])) {
+            rep.minorVersion = 2;
+            break;
+        }
     }
 
     /* From DRI3 proto:
@@ -576,6 +595,51 @@ proc_dri3_set_drm_device_in_use(ClientPtr client)
     return Success;
 }
 
+static int
+proc_dri3_import_syncobj(ClientPtr client)
+{
+    REQUEST(xDRI3ImportSyncobjReq);
+    DrawablePtr drawable;
+    ScreenPtr screen;
+    int fd;
+    int status;
+
+    SetReqFds(client, 1);
+    REQUEST_SIZE_MATCH(xDRI3ImportSyncobjReq);
+    LEGAL_NEW_RESOURCE(stuff->syncobj, client);
+
+    status = dixLookupDrawable(&drawable, stuff->drawable, client,
+                               M_ANY, DixGetAttrAccess);
+    if (status != Success)
+        return status;
+
+    screen = drawable->pScreen;
+
+    fd = ReadFdFromClient(client);
+    if (fd < 0)
+        return BadValue;
+
+    return dri3_import_syncobj(client, screen, stuff->syncobj, fd);
+}
+
+static int
+proc_dri3_free_syncobj(ClientPtr client)
+{
+    REQUEST(xDRI3FreeSyncobjReq);
+    struct dri3_syncobj *syncobj;
+    int status;
+
+    REQUEST_SIZE_MATCH(xDRI3FreeSyncobjReq);
+
+    status = dixLookupResourceByType((void **) &syncobj, stuff->syncobj,
+                                     dri3_syncobj_type, client, DixWriteAccess);
+    if (status != Success)
+        return status;
+
+    FreeResource(stuff->syncobj, RT_NONE);
+    return Success;
+}
+
 int (*proc_dri3_vector[DRI3NumberRequests]) (ClientPtr) = {
     proc_dri3_query_version,            /* 0 */
     proc_dri3_open,                     /* 1 */
@@ -587,6 +651,8 @@ int (*proc_dri3_vector[DRI3NumberRequests]) (ClientPtr) = {
     proc_dri3_pixmap_from_buffers,      /* 7 */
     proc_dri3_buffers_from_pixmap,      /* 8 */
     proc_dri3_set_drm_device_in_use,    /* 9 */
+    proc_dri3_import_syncobj,           /* 10 */
+    proc_dri3_free_syncobj,             /* 11 */
 };
 
 int
@@ -731,6 +797,29 @@ sproc_dri3_set_drm_device_in_use(ClientPtr client)
     return (*proc_dri3_vector[stuff->dri3ReqType]) (client);
 }
 
+static int _X_COLD
+sproc_dri3_import_syncobj(ClientPtr client)
+{
+    REQUEST(xDRI3ImportSyncobjReq);
+    REQUEST_SIZE_MATCH(xDRI3ImportSyncobjReq);
+
+    swaps(&stuff->length);
+    swapl(&stuff->syncobj);
+    swapl(&stuff->drawable);
+    return (*proc_dri3_vector[stuff->dri3ReqType]) (client);
+}
+
+static int _X_COLD
+sproc_dri3_free_syncobj(ClientPtr client)
+{
+    REQUEST(xDRI3FreeSyncobjReq);
+    REQUEST_SIZE_MATCH(xDRI3FreeSyncobjReq);
+
+    swaps(&stuff->length);
+    swapl(&stuff->syncobj);
+    return (*proc_dri3_vector[stuff->dri3ReqType]) (client);
+}
+
 int (*sproc_dri3_vector[DRI3NumberRequests]) (ClientPtr) = {
     sproc_dri3_query_version,           /* 0 */
     sproc_dri3_open,                    /* 1 */
@@ -742,6 +831,8 @@ int (*sproc_dri3_vector[DRI3NumberRequests]) (ClientPtr) = {
     sproc_dri3_pixmap_from_buffers,     /* 7 */
     sproc_dri3_buffers_from_pixmap,     /* 8 */
     sproc_dri3_set_drm_device_in_use,   /* 9 */
+    sproc_dri3_import_syncobj,          /* 10 */
+    sproc_dri3_free_syncobj,            /* 11 */
 };
 
 int _X_COLD
diff --git a/dri3/dri3_screen.c b/dri3/dri3_screen.c
index bc96e5339..5614a22b3 100644
--- a/dri3/dri3_screen.c
+++ b/dri3/dri3_screen.c
@@ -272,3 +272,23 @@ dri3_get_supported_modifiers(ScreenPtr screen, DrawablePtr drawable,
 
     return Success;
 }
+
+int dri3_import_syncobj(ClientPtr client, ScreenPtr screen, XID id, int fd)
+{
+    const dri3_screen_info_rec *info = dri3_screen_priv(screen)->info;
+    struct dri3_syncobj *syncobj = NULL;
+
+    if (info->version < 4 || !info->import_syncobj)
+        return BadImplementation;
+
+    syncobj = info->import_syncobj(client, screen, id, fd);
+    close(fd);
+
+    if (!syncobj)
+        return BadAlloc;
+
+    if (!AddResource(id, dri3_syncobj_type, syncobj))
+        return BadAlloc;
+
+    return Success;
+}
diff --git a/include/protocol-versions.h b/include/protocol-versions.h
index ee58e8a67..6d1b4daa5 100644
--- a/include/protocol-versions.h
+++ b/include/protocol-versions.h
@@ -52,7 +52,7 @@
 
 /* DRI3 */
 #define SERVER_DRI3_MAJOR_VERSION               1
-#define SERVER_DRI3_MINOR_VERSION               3
+#define SERVER_DRI3_MINOR_VERSION               4
 
 /* Generic event extension */
 #define SERVER_GE_MAJOR_VERSION                 1
diff --git a/meson.build b/meson.build
index 77721b411..cd52cefaf 100644
--- a/meson.build
+++ b/meson.build
@@ -91,7 +91,7 @@ scrnsaverproto_dep = dependency('scrnsaverproto', version: '>= 1.1', fallback: [
 resourceproto_dep = dependency('resourceproto', version: '>= 1.2.0', fallback: ['xorgproto', 'ext_xorgproto'])
 xf86driproto_dep = dependency('xf86driproto', version: '>= 2.1.0', fallback: ['xorgproto', 'ext_xorgproto'], required: get_option('dri1') == 'true')
 dri2proto_dep = dependency('dri2proto', version: '>= 2.8', fallback: ['xorgproto', 'ext_xorgproto'], required: get_option('dri2') == 'true')
-dri3proto_dep = dependency('dri3proto', version: '>= 1.3', fallback: ['xorgproto', 'ext_xorgproto'], required: get_option('dri3') == 'true')
+dri3proto_dep = dependency('dri3proto', version: '>= 1.4', fallback: ['xorgproto', 'ext_xorgproto'], required: get_option('dri3') == 'true')
 xineramaproto_dep = dependency('xineramaproto', fallback: ['xorgproto', 'ext_xorgproto'])
 xf86bigfontproto_dep = dependency('xf86bigfontproto', version: '>= 1.2.0', fallback: ['xorgproto', 'ext_xorgproto'], required: get_option('xf86bigfont'))
 xf86vidmodeproto_dep = dependency('xf86vidmodeproto', version: '>= 2.2.99.1', fallback: ['xorgproto', 'ext_xorgproto'])
commit f051a2449dd8c009b780cb5d1c9e77fee6f4c01e
Author: Erik Kurzinger <ekurzinger at nvidia.com>
Date:   Tue Oct 24 16:14:17 2023 -0700

    DRI3: provide stub implementation of DRI3SetDRMDeviceInUse
    
    DRI3 version 1.3 introduced a new request which allows clients to
    provide a hint to the server about which DRM device they are using, so
    that the server might return DRM format modifiers specific to that
    device. However, implementing such functionality, for Xwayland in
    particular, will require fairly significant architectural changes.
    
    To avoid blocking future versions of the DRI3 extension, we provide here
    a stub implementation for the request in question. The spec explicitly
    states that it is only a hint that the server is free to ignore, so
    strictly speaking this implementation is still correct.
    
    Signed-off-by: Erik Kurzinger <ekurzinger at nvidia.com>
    Part-of: <https://gitlab.freedesktop.org/xorg/xserver/-/merge_requests/967>

diff --git a/dri3/dri3_request.c b/dri3/dri3_request.c
index 99ff73d39..a5cc7ccc9 100644
--- a/dri3/dri3_request.c
+++ b/dri3/dri3_request.c
@@ -555,6 +555,27 @@ proc_dri3_buffers_from_pixmap(ClientPtr client)
     return Success;
 }
 
+static int
+proc_dri3_set_drm_device_in_use(ClientPtr client)
+{
+    REQUEST(xDRI3SetDRMDeviceInUseReq);
+    WindowPtr window;
+    int status;
+
+    REQUEST_SIZE_MATCH(xDRI3SetDRMDeviceInUseReq);
+    status = dixLookupWindow(&window, stuff->window, client,
+                             DixGetAttrAccess);
+    if (status != Success)
+        return status;
+
+    /* TODO Eventually we should use this information to have
+     * DRI3GetSupportedModifiers return device-specific modifiers, but for now
+     * we will ignore it until multi-device support is more complete.
+     * Otherwise we can't advertise support for DRI3 1.4.
+     */
+    return Success;
+}
+
 int (*proc_dri3_vector[DRI3NumberRequests]) (ClientPtr) = {
     proc_dri3_query_version,            /* 0 */
     proc_dri3_open,                     /* 1 */
@@ -565,6 +586,7 @@ int (*proc_dri3_vector[DRI3NumberRequests]) (ClientPtr) = {
     proc_dri3_get_supported_modifiers,  /* 6 */
     proc_dri3_pixmap_from_buffers,      /* 7 */
     proc_dri3_buffers_from_pixmap,      /* 8 */
+    proc_dri3_set_drm_device_in_use,    /* 9 */
 };
 
 int
@@ -698,6 +720,17 @@ sproc_dri3_buffers_from_pixmap(ClientPtr client)
     return (*proc_dri3_vector[stuff->dri3ReqType]) (client);
 }
 
+static int _X_COLD
+sproc_dri3_set_drm_device_in_use(ClientPtr client)
+{
+    REQUEST(xDRI3SetDRMDeviceInUseReq);
+    REQUEST_SIZE_MATCH(xDRI3SetDRMDeviceInUseReq);
+    swapl(&stuff->window);
+    swapl(&stuff->drmMajor);
+    swapl(&stuff->drmMinor);
+    return (*proc_dri3_vector[stuff->dri3ReqType]) (client);
+}
+
 int (*sproc_dri3_vector[DRI3NumberRequests]) (ClientPtr) = {
     sproc_dri3_query_version,           /* 0 */
     sproc_dri3_open,                    /* 1 */
@@ -708,6 +741,7 @@ int (*sproc_dri3_vector[DRI3NumberRequests]) (ClientPtr) = {
     sproc_dri3_get_supported_modifiers, /* 6 */
     sproc_dri3_pixmap_from_buffers,     /* 7 */
     sproc_dri3_buffers_from_pixmap,     /* 8 */
+    sproc_dri3_set_drm_device_in_use,   /* 9 */
 };
 
 int _X_COLD
diff --git a/include/protocol-versions.h b/include/protocol-versions.h
index a81a2d17c..ee58e8a67 100644
--- a/include/protocol-versions.h
+++ b/include/protocol-versions.h
@@ -52,7 +52,7 @@
 
 /* DRI3 */
 #define SERVER_DRI3_MAJOR_VERSION               1
-#define SERVER_DRI3_MINOR_VERSION               2
+#define SERVER_DRI3_MINOR_VERSION               3
 
 /* Generic event extension */
 #define SERVER_GE_MAJOR_VERSION                 1
diff --git a/meson.build b/meson.build
index 86c1e55cf..77721b411 100644
--- a/meson.build
+++ b/meson.build
@@ -91,7 +91,7 @@ scrnsaverproto_dep = dependency('scrnsaverproto', version: '>= 1.1', fallback: [
 resourceproto_dep = dependency('resourceproto', version: '>= 1.2.0', fallback: ['xorgproto', 'ext_xorgproto'])
 xf86driproto_dep = dependency('xf86driproto', version: '>= 2.1.0', fallback: ['xorgproto', 'ext_xorgproto'], required: get_option('dri1') == 'true')
 dri2proto_dep = dependency('dri2proto', version: '>= 2.8', fallback: ['xorgproto', 'ext_xorgproto'], required: get_option('dri2') == 'true')
-dri3proto_dep = dependency('dri3proto', version: '>= 1.2', fallback: ['xorgproto', 'ext_xorgproto'], required: get_option('dri3') == 'true')
+dri3proto_dep = dependency('dri3proto', version: '>= 1.3', fallback: ['xorgproto', 'ext_xorgproto'], required: get_option('dri3') == 'true')
 xineramaproto_dep = dependency('xineramaproto', fallback: ['xorgproto', 'ext_xorgproto'])
 xf86bigfontproto_dep = dependency('xf86bigfontproto', version: '>= 1.2.0', fallback: ['xorgproto', 'ext_xorgproto'], required: get_option('xf86bigfont'))
 xf86vidmodeproto_dep = dependency('xf86vidmodeproto', version: '>= 2.2.99.1', fallback: ['xorgproto', 'ext_xorgproto'])
commit e00e7205c94be530a008d8122472c882b9382eb8
Author: Erik Kurzinger <ekurzinger at nvidia.com>
Date:   Tue Mar 26 09:53:17 2024 -0700

    Update CI for Xwayland explicit sync
    
    Explicit sync support for Xwayland requires several updates to the
    Debian CI image. These include...
    
    xorgproto 2024.1 for DRI3 1.4 and Present 1.4
    wayland-protocols 1.34 for wp-linux-drm-syncobj-v1
    libdrm 2.4.116 for drmSyncobjEventfd
    linux-libc-dev from bullseye-backports for DMA_BUF_IOCTL_IMPORT/EXPORT_SYNC_FD
    
    Signed-off-by: Erik Kurzinger <ekurzinger at nvidia.com>
    Part-of: <https://gitlab.freedesktop.org/xorg/xserver/-/merge_requests/967>

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 8aebd5d5b..dd055e284 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -19,7 +19,7 @@ variables:
     FDO_UPSTREAM_REPO: xorg/xserver
     FDO_DISTRIBUTION_VERSION: bullseye-slim
     FDO_DISTRIBUTION_EXEC: 'env FDO_CI_CONCURRENT=${FDO_CI_CONCURRENT} bash .gitlab-ci/debian-install.sh'
-    FDO_DISTRIBUTION_TAG: "2024-02-29-libei.0"
+    FDO_DISTRIBUTION_TAG: "2024-03-26-explicit-sync-2"
     MESON_BUILDDIR: "build"
 
 include:
diff --git a/.gitlab-ci/cross-prereqs-build.sh b/.gitlab-ci/cross-prereqs-build.sh
index dabc041ed..916d95a3d 100755
--- a/.gitlab-ci/cross-prereqs-build.sh
+++ b/.gitlab-ci/cross-prereqs-build.sh
@@ -49,7 +49,7 @@ build 'https://gitlab.freedesktop.org/pixman/pixman.git' 'pixman-0.38.4'
 build 'https://gitlab.freedesktop.org/xorg/lib/pthread-stubs.git' '0.4'
 # we can't use the xorgproto pkgconfig files from /usr/share/pkgconfig, because
 # these would add -I/usr/include to CFLAGS, which breaks cross-compilation
-build 'https://gitlab.freedesktop.org/xorg/proto/xorgproto.git' 'xorgproto-2023.2' '--datadir=/lib'
+build 'https://gitlab.freedesktop.org/xorg/proto/xorgproto.git' 'xorgproto-2024.1' '--datadir=/lib'
 build 'https://gitlab.freedesktop.org/xorg/lib/libXau.git' 'libXau-1.0.9'
 build 'https://gitlab.freedesktop.org/xorg/proto/xcbproto.git' 'xcb-proto-1.14.1'
 build 'https://gitlab.freedesktop.org/xorg/lib/libxcb.git' 'libxcb-1.14'
diff --git a/.gitlab-ci/debian-install.sh b/.gitlab-ci/debian-install.sh
index 0ff294a7b..59bcea4b0 100644
--- a/.gitlab-ci/debian-install.sh
+++ b/.gitlab-ci/debian-install.sh
@@ -15,6 +15,10 @@ EPHEMERAL="
 	xvfb
 	"
 
+# Add bullseye-backports for the newer linux-libc-dev package
+echo 'deb http://deb.debian.org/debian bullseye-backports main' >> /etc/apt/sources.list
+apt update
+
 apt-get install -y \
 	$EPHEMERAL \
 	autoconf \
@@ -93,6 +97,7 @@ apt-get install -y \
 	libxtst-dev \
 	libxv-dev \
 	libz-mingw-w64-dev \
+	linux-libc-dev/bullseye-backports \
 	mesa-common-dev \
 	meson \
 	mingw-w64-tools \
@@ -114,8 +119,8 @@ apt-get install -y \
 
 cd /root
 
-# Xwayland requires drm 2.4.109 for drmGetDeviceFromDevId
-git clone https://gitlab.freedesktop.org/mesa/drm --depth 1 --branch=libdrm-2.4.109
+# Xwayland requires drm 2.4.116 for drmSyncobjEventfd
+git clone https://gitlab.freedesktop.org/mesa/drm --depth 1 --branch=libdrm-2.4.116
 cd drm
 meson _build
 ninja -C _build -j${FDO_CI_CONCURRENT:-4} install
@@ -130,8 +135,8 @@ ninja -C _build -j${FDO_CI_CONCURRENT:-4} install
 cd ..
 rm -rf libxcvt
 
-# xserver requires xorgproto >= 2023.2 for XWAYLAND
-git clone https://gitlab.freedesktop.org/xorg/proto/xorgproto.git --depth 1 --branch=xorgproto-2023.2
+# xserver requires xorgproto >= 2024.1 for XWAYLAND
+git clone https://gitlab.freedesktop.org/xorg/proto/xorgproto.git --depth 1 --branch=xorgproto-2024.1
 pushd xorgproto
 ./autogen.sh
 make -j${FDO_CI_CONCURRENT:-4} install
@@ -146,8 +151,8 @@ ninja -C _build -j${FDO_CI_CONCURRENT:-4} install
 cd ..
 rm -rf wayland
 
-# Xwayland requires wayland-protocols >= 1.30, but Debian bullseye has 1.20 only
-git clone https://gitlab.freedesktop.org/wayland/wayland-protocols.git --depth 1 --branch=1.30
+# Xwayland requires wayland-protocols >= 1.34, but Debian bullseye has 1.20 only
+git clone https://gitlab.freedesktop.org/wayland/wayland-protocols.git --depth 1 --branch=1.34
 cd wayland-protocols
 meson _build
 ninja -C _build -j${FDO_CI_CONCURRENT:-4} install
commit d411a8b611aab57b2ac068928ddc09bef2b2a097
Author: Xaver Hugl <xaver.hugl at kde.org>
Date:   Mon Mar 4 17:15:13 2024 +0100

    xwayland: add workaround for drivers that don't support impicit sync
    
    Without either implicit or explicit synchronization, the result of rendering is
    pretty much undefined, and many glitches can appear. This still doesn't synchronize
    buffer release, but it works around most glitches until explicit sync is supported.
    
    Signed-off-by: Xaver Hugl <xaver.hugl at kde.org>
    Part-of: <https://gitlab.freedesktop.org/xorg/xserver/-/merge_requests/967>

diff --git a/hw/xwayland/xwayland-present.c b/hw/xwayland/xwayland-present.c
index d973dea23..91dc396fb 100644
--- a/hw/xwayland/xwayland-present.c
+++ b/hw/xwayland/xwayland-present.c
@@ -695,7 +695,10 @@ xwl_present_check_flip(RRCrtcPtr crtc,
     if (xwl_window->xwl_screen->glamor &&
         !xwl_glamor_check_flip(present_window, pixmap))
         return FALSE;
-#endif
+
+    if (!xwl_glamor_supports_implicit_sync(xwl_window->xwl_screen))
+        return FALSE;
+#endif /* XWL_HAS_GLAMOR */
 
     /* Can't flip if the window pixmap doesn't match the xwl_window parent
      * window's, e.g. because a client redirected this window or one of its
diff --git a/hw/xwayland/xwayland-window-buffers.c b/hw/xwayland/xwayland-window-buffers.c
index ced9d9567..85c987de7 100644
--- a/hw/xwayland/xwayland-window-buffers.c
+++ b/hw/xwayland/xwayland-window-buffers.c
@@ -32,6 +32,9 @@
 #include "xwayland-pixmap.h"
 #include "xwayland-screen.h"
 #include "xwayland-window-buffers.h"
+#ifdef XWL_HAS_GLAMOR
+#include "glamor.h"
+#endif
 
 #define BUFFER_TIMEOUT 1 * 1000 /* ms */
 
@@ -384,6 +387,12 @@ xwl_window_swap_pixmap(struct xwl_window *xwl_window)
 
     /* Hold a reference on the buffer until it's released by the compositor */
     xwl_window_buffer->refcnt++;
+
+#ifdef XWL_HAS_GLAMOR
+    if (!xwl_glamor_supports_implicit_sync(xwl_screen)) {
+        glamor_finish(xwl_screen->screen);
+    }
+#endif /* XWL_HAS_GLAMOR */
     xwl_pixmap_set_buffer_release_cb(xwl_window_buffer->pixmap,
                                      xwl_window_buffer_release_callback,
                                      xwl_window_buffer);
commit 89c327f2632f3e54ee9a6cd8a5443aa0e878a04a
Author: Erik Kurzinger <ekurzinger at nvidia.com>
Date:   Mon Mar 4 17:11:59 2024 +0100

    xwayland: add detection for drivers that don't support implicit sync
    
    Signed-off-by: Erik Kurzinger <ekurzinger at nvidia.com>
    Part-of: <https://gitlab.freedesktop.org/xorg/xserver/-/merge_requests/967>

diff --git a/hw/xwayland/xwayland-glamor-gbm.c b/hw/xwayland/xwayland-glamor-gbm.c
index 7cd4a6806..b22e89248 100644
--- a/hw/xwayland/xwayland-glamor-gbm.c
+++ b/hw/xwayland/xwayland-glamor-gbm.c
@@ -62,6 +62,7 @@ struct xwl_gbm_private {
     Bool drm_authenticated;
     Bool dmabuf_capable;
     Bool glamor_gles;
+    Bool implicit_sync;
 
     /* Set if wl_drm is available */
     struct wl_drm *drm;
@@ -1020,6 +1021,14 @@ xwl_glamor_gbm_has_egl_extension(void)
             epoxy_has_egl_extension(NULL, "EGL_KHR_platform_gbm"));
 }
 
+Bool
+xwl_glamor_supports_implicit_sync(struct xwl_screen *xwl_screen)
+{
+    /* absent glamor, implicit sync is irrelevant so just return TRUE */
+    return !xwl_screen->glamor ||
+        xwl_gbm_get(xwl_screen)->implicit_sync;
+}
+
 static Bool
 xwl_glamor_try_to_make_context_current(struct xwl_screen *xwl_screen)
 {
@@ -1164,7 +1173,7 @@ xwl_glamor_gbm_init_egl(struct xwl_screen *xwl_screen)
     struct xwl_gbm_private *xwl_gbm = xwl_gbm_get(xwl_screen);
     EGLint major, minor;
     const GLubyte *renderer;
-    const char *gbm_backend_name;
+    const char *gbm_backend_name, *egl_vendor;
 
     if (!xwl_gbm->drm && !xwl_glamor_gbm_init_main_dev(xwl_screen))
         return FALSE;
@@ -1229,6 +1238,14 @@ xwl_glamor_gbm_init_egl(struct xwl_screen *xwl_screen)
         xwl_screen->glvnd_vendor = gbm_backend_name;
     xwl_gbm->glamor_gles = !epoxy_is_desktop_gl();
 
+    egl_vendor = eglQueryString(xwl_screen->egl_display, EGL_VENDOR);
+    if (!egl_vendor) {
+        ErrorF("Could not determine EGL vendor\n");
+        goto error;
+    }
+    /* NVIDIA driver does not support implicit sync */
+    xwl_gbm->implicit_sync = !strstr(egl_vendor, "NVIDIA");
+
     return TRUE;
 error:
     if (xwl_screen->egl_display != EGL_NO_DISPLAY) {
diff --git a/hw/xwayland/xwayland-glamor.h b/hw/xwayland/xwayland-glamor.h
index 297ae1786..ff019feda 100644
--- a/hw/xwayland/xwayland-glamor.h
+++ b/hw/xwayland/xwayland-glamor.h
@@ -57,6 +57,7 @@ void xwl_glamor_init_wl_registry(struct xwl_screen *xwl_screen,
 void xwl_glamor_egl_make_current(struct xwl_screen *xwl_screen);
 Bool xwl_glamor_check_flip(WindowPtr present_window, PixmapPtr pixmap);
 PixmapPtr xwl_glamor_create_pixmap_for_window (struct xwl_window *xwl_window);
+Bool xwl_glamor_supports_implicit_sync(struct xwl_screen *xwl_screen);
 
 #ifdef XV
 /* glamor Xv Adaptor */


More information about the xorg-commit mailing list