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'