From 6f85ce4d4eeaa7070b9c2cf4536d35cd14d22090 Mon Sep 17 00:00:00 2001 From: Erik Kurzinger Date: Mon, 18 Mar 2024 11:36:35 -0700 Subject: [PATCH] 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 Part-of: --- hw/xwayland/xwayland-glamor-gbm.c | 206 +++++++++++++++++++++++++++++- hw/xwayland/xwayland-glamor.c | 24 ++++ hw/xwayland/xwayland-glamor.h | 2 + hw/xwayland/xwayland-present.c | 63 ++++++++- hw/xwayland/xwayland-screen.h | 2 + meson.build | 2 +- 6 files changed, 292 insertions(+), 7 deletions(-) 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 #include +#include #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'