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@nvidia.com>
Part-of: <https://gitlab.freedesktop.org/xorg/xserver/-/merge_requests/967>
This commit is contained in:
Erik Kurzinger 2024-03-18 11:36:35 -07:00 committed by Olivier Fourdan
parent ac0bc0b3b6
commit 6f85ce4d4e
6 changed files with 292 additions and 7 deletions

View File

@ -66,6 +66,7 @@ struct xwl_gbm_private {
Bool dmabuf_capable; Bool dmabuf_capable;
Bool glamor_gles; Bool glamor_gles;
Bool implicit_sync; Bool implicit_sync;
Bool supports_syncobjs;
/* Set if wl_drm is available */ /* Set if wl_drm is available */
struct wl_drm *drm; struct wl_drm *drm;
@ -960,7 +961,192 @@ xwl_glamor_dmabuf_import_sync_file(PixmapPtr pixmap, int sync_file)
close(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, .version = 2,
.open = NULL, .open = NULL,
.pixmap_from_fds = glamor_pixmap_from_fds, .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_formats = xwl_glamor_get_formats,
.get_modifiers = xwl_glamor_get_modifiers, .get_modifiers = xwl_glamor_get_modifiers,
.get_drawable_modifiers = xwl_glamor_get_drawable_modifiers, .get_drawable_modifiers = xwl_glamor_get_drawable_modifiers,
.import_syncobj = NULL, /* need to check for kernel support */
}; };
static const char * static const char *
@ -1129,6 +1316,13 @@ xwl_glamor_supports_implicit_sync(struct xwl_screen *xwl_screen)
xwl_gbm_get(xwl_screen)->implicit_sync; 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 static Bool
xwl_glamor_try_to_make_context_current(struct xwl_screen *xwl_screen) 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 */ /* NVIDIA driver does not support implicit sync */
xwl_gbm->implicit_sync = !strstr(egl_vendor, "NVIDIA"); 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; return TRUE;
error: error:
if (xwl_screen->egl_display != EGL_NO_DISPLAY) { 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); 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)) { if (!dri3_screen_init(xwl_screen->screen, &xwl_dri3_info)) {
ErrorF("Failed to initialize dri3\n"); ErrorF("Failed to initialize dri3\n");
goto error; goto error;

View File

@ -269,6 +269,30 @@ glamor_egl_fd_name_from_pixmap(ScreenPtr screen,
return 0; 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 Bool
xwl_glamor_init(struct xwl_screen *xwl_screen) xwl_glamor_init(struct xwl_screen *xwl_screen)
{ {

View File

@ -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); Bool xwl_glamor_supports_implicit_sync(struct xwl_screen *xwl_screen);
void xwl_glamor_dmabuf_import_sync_file(PixmapPtr pixmap, int sync_file); void xwl_glamor_dmabuf_import_sync_file(PixmapPtr pixmap, int sync_file);
int xwl_glamor_dmabuf_export_sync_file(PixmapPtr pixmap); 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 #ifdef XV
/* glamor Xv Adaptor */ /* glamor Xv Adaptor */

View File

@ -30,6 +30,7 @@
#endif #endif
#include <windowstr.h> #include <windowstr.h>
#include <present.h> #include <present.h>
#include <sys/eventfd.h>
#include "xwayland-present.h" #include "xwayland-present.h"
#include "xwayland-screen.h" #include "xwayland-screen.h"
@ -225,7 +226,8 @@ xwl_present_queue_vblank(ScreenPtr screen,
static uint32_t static uint32_t
xwl_present_query_capabilities(present_screen_priv_ptr screen_priv) 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 static int
@ -322,7 +324,17 @@ xwl_present_free_event(struct xwl_present_event *event)
static void static void
xwl_present_free_idle_vblank(present_vblank_ptr vblank) 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)); xwl_present_free_event(xwl_present_event_from_vblank(vblank));
} }
@ -483,7 +495,17 @@ xwl_present_buffer_release(void *data)
return; return;
vblank = &event->vblank; 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); xwl_present_window = xwl_present_window_priv(vblank->window);
if (xwl_present_window->flip_active == vblank || 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 static Bool
xwl_present_check_flip(RRCrtcPtr crtc, xwl_present_check_flip(RRCrtcPtr crtc,
WindowPtr present_window, 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); xorg_list_del(&vblank->event_queue);
retry:
if (present_execute_wait(vblank, crtc_msc)) if (present_execute_wait(vblank, crtc_msc))
return; return;
@ -972,6 +1007,8 @@ xwl_present_execute(present_vblank_ptr vblank, uint64_t ust, uint64_t crtc_msc)
} }
vblank->flip = FALSE; vblank->flip = FALSE;
/* re-execute, falling through to copy */
goto retry;
} }
DebugPresent(("\tc %p %" PRIu64 ": %08" PRIx32 " -> %08" PRIx32 "\n", DebugPresent(("\tc %p %" PRIu64 ": %08" PRIx32 " -> %08" PRIx32 "\n",
vblank, crtc_msc, vblank->pixmap->drawable.id, vblank->window->drawable.id)); 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; ScreenPtr screen = window->drawable.pScreen;
present_window_priv_ptr window_priv = present_get_window_priv(window, TRUE); present_window_priv_ptr window_priv = present_get_window_priv(window, TRUE);
present_screen_priv_ptr screen_priv = present_screen_priv(screen); 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; struct xwl_present_event *event;
if (!window_priv) if (!window_priv)
return BadAlloc; return BadAlloc;
#ifdef DRI3 #ifdef DRI3
if (acquire_syncobj || release_syncobj) if (!(caps & PresentCapabilitySyncobj) &&
(acquire_syncobj || release_syncobj))
return BadValue; return BadValue;
#endif /* DRI3 */ #endif /* DRI3 */
@ -1076,6 +1116,10 @@ xwl_present_pixmap(WindowPtr window,
if (xwl_present_event_from_vblank(vblank)->copy_executed) if (xwl_present_event_from_vblank(vblank)->copy_executed)
continue; continue;
if (vblank->release_syncobj)
vblank->release_syncobj->signal(vblank->release_syncobj,
vblank->release_point);
present_vblank_scrap(vblank); present_vblank_scrap(vblank);
if (vblank->flip_ready) if (vblank->flip_ready)
xwl_present_re_execute(vblank); xwl_present_re_execute(vblank);
@ -1092,7 +1136,7 @@ xwl_present_pixmap(WindowPtr window,
#ifdef DRI3 #ifdef DRI3
acquire_syncobj, release_syncobj, acquire_point, release_point, acquire_syncobj, release_syncobj, acquire_point, release_point,
#endif /* DRI3 */ #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); present_vblank_destroy(vblank);
return BadAlloc; return BadAlloc;
} }
@ -1131,6 +1175,7 @@ xwl_present_unrealize_window(struct xwl_present_window *xwl_present_window)
Bool Bool
xwl_present_init(ScreenPtr screen) xwl_present_init(ScreenPtr screen)
{ {
struct xwl_screen *xwl_screen = xwl_screen_get(screen);
present_screen_priv_ptr screen_priv; present_screen_priv_ptr screen_priv;
if (!present_screen_register_priv_keys()) 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)) if (!dixRegisterPrivateKey(&xwl_present_window_private_key, PRIVATE_WINDOW, 0))
return FALSE; 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->query_capabilities = xwl_present_query_capabilities;
screen_priv->get_crtc = xwl_present_get_crtc; 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->present_pixmap = xwl_present_pixmap;
screen_priv->queue_vblank = xwl_present_queue_vblank; screen_priv->queue_vblank = xwl_present_queue_vblank;
screen_priv->flush = xwl_present_flush; screen_priv->flush = xwl_present_flush;
screen_priv->flush_fenced = xwl_present_flush_fenced;
screen_priv->re_execute = xwl_present_re_execute; screen_priv->re_execute = xwl_present_re_execute;
screen_priv->abort_vblank = xwl_present_abort_vblank; screen_priv->abort_vblank = xwl_present_abort_vblank;

View File

@ -142,6 +142,8 @@ struct xwl_screen {
struct libdecor *libdecor_context; struct libdecor *libdecor_context;
#endif #endif
const char *output_name; const char *output_name;
uint32_t present_capabilities;
}; };
/* Apps which use randr/vidmode to change the mode when going fullscreen, /* Apps which use randr/vidmode to change the mode when going fullscreen,

View File

@ -60,7 +60,7 @@ endforeach
add_project_arguments(common_wflags, language : ['c', 'objc']) add_project_arguments(common_wflags, language : ['c', 'objc'])
libdrm_req = '>= 2.4.109' libdrm_req = '>= 2.4.116'
libselinux_req = '>= 2.0.86' libselinux_req = '>= 2.0.86'
xext_req = '>= 1.0.99.4' xext_req = '>= 1.0.99.4'
wayland_req = '>= 1.21.0' wayland_req = '>= 1.21.0'