From f7a968ef9c6b5eadbf1ba12d46ef20609c6f65aa Mon Sep 17 00:00:00 2001 From: Erik Kurzinger Date: Tue, 16 Aug 2022 11:57:40 -0700 Subject: [PATCH 1/3] 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 * check - poll a timeline point and return whether it 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 Implementations will be responsible for populating these function pointers when importing a syncobj. Signed-off-by: Erik Kurzinger --- dri3/dri3.c | 15 +++++++++ dri3/dri3.h | 33 ++++++++++++++++++++ dri3/dri3_priv.h | 3 ++ dri3/dri3_request.c | 74 +++++++++++++++++++++++++++++++++++++++++++++ dri3/dri3_screen.c | 18 +++++++++++ 5 files changed, 143 insertions(+) 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..f738fe8f5 100644 --- a/dri3/dri3.h +++ b/dri3/dri3.h @@ -30,6 +30,31 @@ #define DRI3_SCREEN_INFO_VERSION 2 +extern RESTYPE dri3_syncobj_type; + +struct dri3_syncobj +{ + XID id; + ScreenPtr screen; + uint32_t refcount; + + void (*free)(struct dri3_syncobj *syncobj); + Bool (*check)(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); +}; + +#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, int *fd); @@ -84,6 +109,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 +131,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 687168930..e7218b8b9 100644 --- a/dri3/dri3_request.c +++ b/dri3/dri3_request.c @@ -554,6 +554,51 @@ proc_dri3_buffers_from_pixmap(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, dri3_syncobj_type); + return Success; +} + int (*proc_dri3_vector[DRI3NumberRequests]) (ClientPtr) = { proc_dri3_query_version, /* 0 */ proc_dri3_open, /* 1 */ @@ -564,6 +609,9 @@ int (*proc_dri3_vector[DRI3NumberRequests]) (ClientPtr) = { proc_dri3_get_supported_modifiers, /* 6 */ proc_dri3_pixmap_from_buffers, /* 7 */ proc_dri3_buffers_from_pixmap, /* 8 */ + NULL, /* 9 */ + proc_dri3_import_syncobj, /* 10 */ + proc_dri3_free_syncobj, /* 11 */ }; int @@ -697,6 +745,29 @@ sproc_dri3_buffers_from_pixmap(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 */ @@ -707,6 +778,9 @@ int (*sproc_dri3_vector[DRI3NumberRequests]) (ClientPtr) = { sproc_dri3_get_supported_modifiers, /* 6 */ sproc_dri3_pixmap_from_buffers, /* 7 */ sproc_dri3_buffers_from_pixmap, /* 8 */ + NULL, /* 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 3c7e5bf60..57df426ac 100644 --- a/dri3/dri3_screen.c +++ b/dri3/dri3_screen.c @@ -304,3 +304,21 @@ 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->import_syncobj) + return BadImplementation; + + syncobj = info->import_syncobj(client, screen, id, fd); + if (!syncobj) + return BadAlloc; + + if (!AddResource(id, dri3_syncobj_type, syncobj)) + return BadAlloc; + + return Success; +} -- GitLab From 12589e9c5c3a6eccd7dd0c7b5b99f811fd8fe3dc Mon Sep 17 00:00:00 2001 From: Erik Kurzinger Date: Tue, 16 Aug 2022 11:59:14 -0700 Subject: [PATCH 2/3] xwayland: implement support for DRI3 syncobjs Adds an implementation of the import_syncobj DRI3 screen info callback for Xwayland along with the various operations defined for dri3_syncobj objects. Freeing, checking, and signaling are fairly simple wrappers around the associated libdrm functions. Importing or exporting a sync fd to or from a particular timeline point requires the use of a temporary binary syncobj whose fence is then transfered to the target timeline syncobj, since the userspace API does not provide a way to do this directly. Signed-off-by: Erik Kurzinger --- hw/xwayland/xwayland-glamor-gbm.c | 112 ++++++++++++++++++++++++++++++ 1 file changed, 112 insertions(+) diff --git a/hw/xwayland/xwayland-glamor-gbm.c b/hw/xwayland/xwayland-glamor-gbm.c index 60c63ba99..93610d9a8 100644 --- a/hw/xwayland/xwayland-glamor-gbm.c +++ b/hw/xwayland/xwayland-glamor-gbm.c @@ -52,6 +52,8 @@ #include "linux-dmabuf-unstable-v1-client-protocol.h" +static uint32_t scratch_syncobj = 0; + struct xwl_gbm_private { char *device_name; struct gbm_device *gbm; @@ -438,6 +440,9 @@ 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 (scratch_syncobj) + drmSyncobjDestroy(xwl_gbm->drm_fd, + scratch_syncobj); free(xwl_gbm); } @@ -652,6 +657,105 @@ glamor_egl_fd_from_pixmap(ScreenPtr screen, PixmapPtr pixmap, return -1; } +struct xwl_syncobj +{ + struct dri3_syncobj base; + uint32_t handle; +}; + +static Bool xwl_dri3_check_syncobj(struct dri3_syncobj *syncobj, + uint64_t point) +{ + struct xwl_syncobj *xwl_syncobj = (struct xwl_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 */, 0 /* flags */, + NULL /* first_signaled */); +} + +static int xwl_dri3_syncobj_export_fence(struct dri3_syncobj *syncobj, + uint64_t point) +{ + struct xwl_syncobj *xwl_syncobj = (struct xwl_syncobj *)syncobj; + struct xwl_screen *xwl_screen = xwl_screen_get(syncobj->screen); + struct xwl_gbm_private *xwl_gbm = xwl_gbm_get(xwl_screen); + int fd = -1; + + if (!drmSyncobjTransfer(xwl_gbm->drm_fd, + scratch_syncobj, 0, + xwl_syncobj->handle, point, + 0 /* flags */)) + drmSyncobjExportSyncFile(xwl_gbm->drm_fd, scratch_syncobj, &fd); + + return fd; +} + +static void xwl_dri3_syncobj_import_fence(struct dri3_syncobj *syncobj, + uint64_t point, int fd) +{ + struct xwl_syncobj *xwl_syncobj = (struct xwl_syncobj *)syncobj; + struct xwl_screen *xwl_screen = xwl_screen_get(syncobj->screen); + struct xwl_gbm_private *xwl_gbm = xwl_gbm_get(xwl_screen); + + if (!drmSyncobjImportSyncFile(xwl_gbm->drm_fd, scratch_syncobj, fd)) + drmSyncobjTransfer(xwl_gbm->drm_fd, + xwl_syncobj->handle, point, + scratch_syncobj, 0, + 0 /* flags */); + close(fd); +} + +static void xwl_dri3_signal_syncobj(struct dri3_syncobj *syncobj, + uint64_t point) +{ + struct xwl_syncobj *xwl_syncobj = (struct xwl_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_syncobj *xwl_syncobj = (struct xwl_syncobj *)syncobj; + struct xwl_screen *xwl_screen = xwl_screen_get(syncobj->screen); + struct xwl_gbm_private *xwl_gbm = xwl_gbm_get(xwl_screen); + + drmSyncobjDestroy(xwl_gbm->drm_fd, xwl_syncobj->handle); + free(xwl_syncobj); +} + +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_syncobj *syncobj = calloc(1, sizeof (*syncobj)); + + if (!syncobj) + return NULL; + + if (drmSyncobjFDToHandle(xwl_gbm->drm_fd, fd, &syncobj->handle)) { + free(syncobj); + return NULL; + } + + syncobj->base.id = id; + syncobj->base.screen = screen; + syncobj->base.refcount = 1; + + syncobj->base.free = xwl_dri3_free_syncobj; + syncobj->base.check = xwl_dri3_check_syncobj; + 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; + return &syncobj->base; +} + static const dri3_screen_info_rec xwl_dri3_info = { .version = 2, .open = NULL, @@ -661,6 +765,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 = glamor_get_drawable_modifiers, + .import_syncobj = xwl_dri3_import_syncobj, }; static const char * @@ -996,6 +1101,13 @@ xwl_glamor_gbm_init_egl(struct xwl_screen *xwl_screen) if (gbm_backend_name && strcmp(gbm_backend_name, "drm") != 0) xwl_screen->glvnd_vendor = gbm_backend_name; + if (drmSyncobjCreate(xwl_gbm->drm_fd, 0 /* flags */, + &scratch_syncobj)) { + ErrorF("failed to create DRM syncobj\n"); + goto error; + } + + return TRUE; error: if (xwl_screen->egl_display != EGL_NO_DISPLAY) { -- GitLab From 4a45433c923e67c30bbb768c0b8ca1d9e257d2d4 Mon Sep 17 00:00:00 2001 From: Erik Kurzinger Date: Tue, 16 Aug 2022 12:03:59 -0700 Subject: [PATCH 3/3] Present: implement PresentPixmapSynced Adds support for an augmented version of PresentPixmap supporting explicit synchronization with the GPU by way of a DRM synchronization object. The new request will accept a previously imported timeline syncobj along with acquire and release points on that timeline. If the backend reports support for PresentCapabilitySyncobj, the syncobj, acquire, and release points will be attached to the vblank structure for the present request, taking an extra reference to the syncobj to ensure it is not freed before the request has completed. Initially, only the Xwayland backend will advertise PresentCapabilitySyncobj, provided that the EGL driver supports the ANDROID_native_fence_sync extension. Before executing a present request, if the given vblank has an attached syncobj, we will first poll the acquire point. If it has not yet been signaled, we will export the fence as a sync fd and add that to the server's event loop. When the fence eventually is signaled, the request will be re-executed. After executing a request, the release point must be signaled after any GPU operations on the pixmap have completed. For flips, this can be done as soon as the buffer has been released by the Wayland compositor (in the case of Xwayland). For copies, we must insert a fence release into the GPU command stream after the copy and transfer it to the syncobj. This requires a new flush_fenced callback, which must be implemented by any backends advertising PresentCapabilitySyncobj. This will perform the same function as the current flush callback, but also return a sync fd corresponding to the aforementioned fence. Signed-off-by: Erik Kurzinger --- hw/xwayland/xwayland-present.c | 57 ++++++++++++-- hw/xwayland/xwayland-screen.h | 2 + present/present.c | 7 ++ present/present_execute.c | 30 +++++++- present/present_priv.h | 20 +++++ present/present_request.c | 131 ++++++++++++++++++++++++++++++++- present/present_scmd.c | 6 ++ present/present_screen.c | 1 + present/present_vblank.c | 23 ++++++ 9 files changed, 270 insertions(+), 7 deletions(-) diff --git a/hw/xwayland/xwayland-present.c b/hw/xwayland/xwayland-present.c index 99e476b2f..23bf7be5d 100644 --- a/hw/xwayland/xwayland-present.c +++ b/hw/xwayland/xwayland-present.c @@ -33,9 +33,7 @@ #include "xwayland-window.h" #include "xwayland-pixmap.h" #include "glamor.h" - - -#define XWL_PRESENT_CAPS PresentCapabilityAsync +#include "glamor_egl.h" /* @@ -173,7 +171,8 @@ xwl_present_execute(present_vblank_ptr vblank, uint64_t ust, uint64_t crtc_msc); 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 @@ -240,6 +239,9 @@ xwl_present_free_event(struct xwl_present_event *event) static void xwl_present_free_idle_vblank(present_vblank_ptr vblank) { + if (vblank->syncobj) + vblank->syncobj->signal(vblank->syncobj, vblank->release_point); + present_pixmap_idle(vblank->pixmap, vblank->window, vblank->serial, vblank->idle_fence); xwl_present_free_event(xwl_present_event_from_id((uintptr_t)vblank)); } @@ -395,6 +397,10 @@ xwl_present_buffer_release(void *data) return; vblank = &event->vblank; + + if (vblank->syncobj) + vblank->syncobj->signal(vblank->syncobj, vblank->release_point); + present_pixmap_idle(vblank->pixmap, vblank->window, vblank->serial, vblank->idle_fence); xwl_present_window = xwl_present_window_priv(vblank->window); @@ -559,6 +565,31 @@ xwl_present_flush(WindowPtr window) glamor_block_handler(window->drawable.pScreen); } +static int +xwl_present_flush_fenced(WindowPtr window) +{ + struct xwl_window *xwl_window = xwl_window_from_window(window); + struct xwl_screen *xwl_screen = xwl_window->xwl_screen; + EGLint attribs[3]; + EGLSyncKHR sync; + int fence_fd = -1; + assert(xwl_screen->present_capabilities & PresentCapabilitySyncobj); + + 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); + } + + xwl_present_flush(window); + return fence_fd; +} + static Bool xwl_present_check_flip(RRCrtcPtr crtc, WindowPtr present_window, @@ -855,6 +886,9 @@ xwl_present_pixmap(WindowPtr window, RRCrtcPtr target_crtc, SyncFence *wait_fence, SyncFence *idle_fence, + struct dri3_syncobj *syncobj, + uint64_t acquire_point, + uint64_t release_point, uint32_t options, uint64_t target_window_msc, uint64_t divisor, @@ -870,6 +904,7 @@ 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); struct xwl_present_event *event; if (!window_priv) @@ -909,6 +944,9 @@ xwl_present_pixmap(WindowPtr window, if (vblank->target_msc != target_msc) continue; + if (vblank->syncobj) + vblank->syncobj->signal(vblank->syncobj, vblank->release_point); + present_vblank_scrap(vblank); if (vblank->flip_ready) xwl_present_re_execute(vblank); @@ -921,7 +959,9 @@ 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, + target_crtc, wait_fence, idle_fence, + syncobj, acquire_point, release_point, + options, xwl_screen->present_capabilities, notifies, num_notifies, target_msc, crtc_msc)) { present_vblank_destroy(vblank); return BadAlloc; @@ -981,6 +1021,12 @@ xwl_present_init(ScreenPtr screen) if (!dixRegisterPrivateKey(&xwl_present_window_private_key, PRIVATE_WINDOW, 0)) return FALSE; + xwl_screen->present_capabilities = PresentCapabilityAsync; + if (epoxy_has_egl_extension(xwl_screen->egl_display, + "ANDROID_native_fence_sync")) + xwl_screen->present_capabilities |= + PresentCapabilitySyncobj; + screen_priv->query_capabilities = xwl_present_query_capabilities; screen_priv->get_crtc = xwl_present_get_crtc; @@ -991,6 +1037,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 fd201cdf5..b86138223 100644 --- a/hw/xwayland/xwayland-screen.h +++ b/hw/xwayland/xwayland-screen.h @@ -132,6 +132,8 @@ struct xwl_screen { int libdecor_fd; struct libdecor *libdecor_context; #endif + + uint32_t present_capabilities; }; /* Apps which use randr/vidmode to change the mode when going fullscreen, diff --git a/present/present.c b/present/present.c index 271fe32bc..7baba1146 100644 --- a/present/present.c +++ b/present/present.c @@ -230,6 +230,9 @@ present_pixmap(WindowPtr window, RRCrtcPtr target_crtc, SyncFence *wait_fence, SyncFence *idle_fence, + struct dri3_syncobj *syncobj, + uint64_t acquire_point, + uint64_t release_point, uint32_t options, uint64_t window_msc, uint64_t divisor, @@ -250,6 +253,9 @@ present_pixmap(WindowPtr window, target_crtc, wait_fence, idle_fence, + syncobj, + acquire_point, + release_point, options, window_msc, divisor, @@ -272,6 +278,7 @@ present_notify_msc(WindowPtr window, 0, 0, NULL, NULL, NULL, + NULL, 0, 0, divisor == 0 ? PresentOptionAsync : 0, target_msc, divisor, remainder, NULL, 0); } diff --git a/present/present_execute.c b/present/present_execute.c index 68a5878be..77db32fb9 100644 --- a/present/present_execute.c +++ b/present/present_execute.c @@ -37,6 +37,18 @@ present_wait_fence_triggered(void *param) screen_priv->re_execute(vblank); } +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->notify_fd = -1; + screen_priv->re_execute(vblank); +} + Bool present_execute_wait(present_vblank_ptr vblank, uint64_t crtc_msc) { @@ -58,6 +70,17 @@ present_execute_wait(present_vblank_ptr vblank, uint64_t crtc_msc) return TRUE; } } + + if (vblank->syncobj && !vblank->syncobj->check(vblank->syncobj, vblank->acquire_point)) { + int fence_fd = vblank->syncobj->export_fence(vblank->syncobj, vblank->acquire_point); + if (fence_fd >= 0) { + SetNotifyFd(fence_fd, present_syncobj_triggered, X_NOTIFY_READ, vblank); + assert(vblank->notify_fd == -1); + vblank->notify_fd = fence_fd; + return TRUE; + } + } + return FALSE; } @@ -85,7 +108,12 @@ 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); + if (vblank->syncobj) { + int fence_fd = screen_priv->flush_fenced(window); + vblank->syncobj->import_fence(vblank->syncobj, vblank->release_point, fence_fd); + } else { + screen_priv->flush(window); + } present_pixmap_idle(vblank->pixmap, vblank->window, vblank->serial, vblank->idle_fence); } diff --git a/present/present_priv.h b/present/present_priv.h index 6ebd009a2..2bf8cb6b6 100644 --- a/present/present_priv.h +++ b/present/present_priv.h @@ -36,6 +36,7 @@ #include #include #include +#include "dri3.h" #if 0 #define DebugPresent(x) ErrorF x @@ -85,6 +86,10 @@ 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 */ + struct dri3_syncobj *syncobj; + uint64_t acquire_point; + uint64_t release_point; + int notify_fd; }; typedef struct present_screen_priv present_screen_priv_rec, *present_screen_priv_ptr; @@ -119,6 +124,9 @@ typedef int (*present_priv_pixmap_ptr)(WindowPtr window, RRCrtcPtr target_crtc, SyncFence *wait_fence, SyncFence *idle_fence, + struct dri3_syncobj *syncobj, + uint64_t acquire_point, + uint64_t release_point, uint32_t options, uint64_t window_msc, uint64_t divisor, @@ -132,6 +140,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, @@ -142,6 +151,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; @@ -175,6 +185,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; @@ -285,6 +296,9 @@ present_pixmap(WindowPtr window, RRCrtcPtr target_crtc, SyncFence *wait_fence, SyncFence *idle_fence, + struct dri3_syncobj *syncobj, + uint64_t acquire_point, + uint64_t release_point, uint32_t options, uint64_t target_msc, uint64_t divisor, @@ -459,6 +473,9 @@ present_vblank_init(present_vblank_ptr vblank, RRCrtcPtr target_crtc, SyncFence *wait_fence, SyncFence *idle_fence, + struct dri3_syncobj *syncobj, + uint64_t acquire_point, + uint64_t release_point, uint32_t options, const uint32_t capabilities, present_notify_ptr notifies, @@ -477,6 +494,9 @@ present_vblank_create(WindowPtr window, RRCrtcPtr target_crtc, SyncFence *wait_fence, SyncFence *idle_fence, + struct dri3_syncobj *syncobj, + uint64_t acquire_point, + uint64_t release_point, uint32_t options, const uint32_t capabilities, present_notify_ptr notifies, diff --git a/present/present_request.c b/present/present_request.c index f3e5679b5..e13f0ff58 100644 --- a/present/present_request.c +++ b/present/present_request.c @@ -145,7 +145,7 @@ proc_present_pixmap(ClientPtr client) ret = present_pixmap(window, pixmap, stuff->serial, valid, update, stuff->x_off, stuff->y_off, target_crtc, - wait_fence, idle_fence, stuff->options, + wait_fence, idle_fence, NULL, 0, 0, stuff->options, stuff->target_msc, stuff->divisor, stuff->remainder, notifies, nnotifies); if (ret != Success) present_destroy_notifies(notifies, nnotifies); @@ -240,12 +240,104 @@ proc_present_query_capabilities (ClientPtr client) return Success; } +#define VERIFY_SYNCOBJ_OR_NONE(syncobj_ptr, syncobj_id, client, access)\ + do {\ + if ((syncobj_id) == None)\ + (syncobj_ptr) = NULL;\ + else \ + VERIFY_DRI3_SYNCOBJ(syncobj_id, syncobj_ptr, access);\ + } while (0); + +static int +proc_present_pixmap_synced (ClientPtr client) +{ + REQUEST(xPresentPixmapSyncedReq); + WindowPtr window; + PixmapPtr pixmap; + RegionPtr valid = NULL; + RegionPtr update = NULL; + SyncFence *wait_fence; + SyncFence *idle_fence; + struct dri3_syncobj *syncobj; + RRCrtcPtr target_crtc; + int ret; + int nnotifies; + present_notify_ptr notifies = NULL; + + REQUEST_AT_LEAST_SIZE(xPresentPixmapSyncedReq); + ret = dixLookupWindow(&window, stuff->window, client, DixWriteAccess); + if (ret != Success) + return ret; + ret = dixLookupResourceByType((void **) &pixmap, stuff->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_CRTC_OR_NONE(target_crtc, stuff->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_SYNCOBJ_OR_NONE(syncobj, stuff->syncobj, client, DixWriteAccess); + + if (stuff->options & ~(PresentAllOptions)) { + client->errorValue = stuff->options; + return BadValue; + } + + if (stuff->divisor == 0) { + if (stuff->remainder != 0) { + client->errorValue = (CARD32) stuff->remainder; + return BadValue; + } + } else { + if (stuff->remainder >= stuff->divisor) { + client->errorValue = (CARD32) stuff->remainder; + return BadValue; + } + } + + if (stuff->syncobj && + (stuff->acquire_point == 0 || + stuff->release_point == 0 || + stuff->acquire_point >= stuff->release_point)) + return BadValue; + + nnotifies = (client->req_len << 2) - sizeof (xPresentPixmapSyncedReq); + if (nnotifies % sizeof (xPresentNotify)) + return BadLength; + + nnotifies /= sizeof (xPresentNotify); + if (nnotifies) { + ret = present_create_notifies(client, nnotifies, (xPresentNotify *) (stuff + 1), ¬ifies); + 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, + syncobj, stuff->acquire_point, stuff->release_point, + stuff->options, stuff->target_msc, stuff->divisor, stuff->remainder, + notifies, nnotifies); + if (ret != Success) + present_destroy_notifies(notifies, nnotifies); + return ret; +} + 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 */ + proc_present_pixmap_synced, /* 5 */ }; int @@ -325,12 +417,49 @@ sproc_present_query_capabilities (ClientPtr client) return (*proc_present_vector[stuff->presentReqType]) (client); } + +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->wait_fence); + swapl(&stuff->idle_fence); + + swapl(&stuff->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); +} + 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 */ + sproc_present_pixmap_synced, /* 5 */ }; int _X_COLD diff --git a/present/present_scmd.c b/present/present_scmd.c index 239055bc1..5d4b44f59 100644 --- a/present/present_scmd.c +++ b/present/present_scmd.c @@ -674,6 +674,9 @@ present_scmd_pixmap(WindowPtr window, RRCrtcPtr target_crtc, SyncFence *wait_fence, SyncFence *idle_fence, + struct dri3_syncobj *syncobj, + uint64_t acquire_point, + uint64_t release_point, uint32_t options, uint64_t target_window_msc, uint64_t divisor, @@ -755,6 +758,9 @@ present_scmd_pixmap(WindowPtr window, target_crtc, wait_fence, idle_fence, + syncobj, + acquire_point, + release_point, options, screen_priv->info ? screen_priv->info->capabilities : 0, notifies, diff --git a/present/present_screen.c b/present/present_screen.c index 15684eda4..e9d124298 100644 --- a/present/present_screen.c +++ b/present/present_screen.c @@ -178,6 +178,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 a9f17d4b2..4becdbab8 100644 --- a/present/present_vblank.c +++ b/present/present_vblank.c @@ -55,6 +55,9 @@ present_vblank_init(present_vblank_ptr vblank, RRCrtcPtr target_crtc, SyncFence *wait_fence, SyncFence *idle_fence, + struct dri3_syncobj *syncobj, + uint64_t acquire_point, + uint64_t release_point, uint32_t options, const uint32_t capabilities, present_notify_ptr notifies, @@ -106,6 +109,7 @@ present_vblank_init(present_vblank_ptr vblank, vblank->notifies = notifies; vblank->num_notifies = num_notifies; vblank->has_suboptimal = (options & PresentOptionSuboptimal); + vblank->notify_fd = -1; if (pixmap != NULL && !(options & PresentOptionCopy) && @@ -135,6 +139,13 @@ present_vblank_init(present_vblank_ptr vblank, goto no_mem; } + if (capabilities & PresentCapabilitySyncobj) { + vblank->syncobj = syncobj; + vblank->acquire_point = acquire_point; + vblank->release_point = release_point; + ++syncobj->refcount; + } + 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 +169,9 @@ present_vblank_create(WindowPtr window, RRCrtcPtr target_crtc, SyncFence *wait_fence, SyncFence *idle_fence, + struct dri3_syncobj *syncobj, + uint64_t acquire_point, + uint64_t release_point, uint32_t options, const uint32_t capabilities, present_notify_ptr notifies, @@ -172,6 +186,7 @@ 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, + syncobj, acquire_point, release_point, options, capabilities, notifies, num_notifies, target_msc, crtc_msc)) return vblank; @@ -229,5 +244,13 @@ present_vblank_destroy(present_vblank_ptr vblank) if (vblank->notifies) present_destroy_notifies(vblank->notifies, vblank->num_notifies); + if (vblank->notify_fd >= 0) { + SetNotifyFd(vblank->notify_fd, NULL, 0, NULL); + close(vblank->notify_fd); + } + + if (vblank->syncobj && --vblank->syncobj->refcount == 0) + vblank->syncobj->free(vblank->syncobj); + free(vblank); } -- GitLab