diff --git a/include/wlr/render/drm_syncobj.h b/include/wlr/render/drm_syncobj.h new file mode 100644 index 000000000..f879b5929 --- /dev/null +++ b/include/wlr/render/drm_syncobj.h @@ -0,0 +1,71 @@ +#ifndef WLR_RENDER_DRM_SYNCOBJ_H +#define WLR_RENDER_DRM_SYNCOBJ_H + +#include +#include + +/** + * A synchronization timeline. + * + * Timelines are used to synchronize accesses to buffers. Given a producer + * (writing contents to a buffer) and a consumer (reading from the buffer), the + * compositor needs to synchronize back-and-forth between these two users. The + * consumer needs to wait for the producer to signal that they're done with the + * writes, and the producer needs to wait for the consumer to signal that + * they're done with the reads. + * + * Timelines provide synchronization points in the form of monotonically + * increasing 64-bit integer values. + * + * wlroots timelines are designed after Vulkan timeline semaphores. For more + * information on the Vulkan APIs, see: + * https://www.khronos.org/blog/vulkan-timeline-semaphores + * + * wlroots timelines are powered by DRM synchronization objects (drm_syncobj): + * https://dri.freedesktop.org/docs/drm/gpu/drm-mm.html#drm-sync-objects + */ +struct wlr_drm_syncobj_timeline { + int drm_fd; + uint32_t handle; + + // private state + + size_t n_refs; +}; + +/** + * Create a new synchronization timeline. + */ +struct wlr_drm_syncobj_timeline *wlr_drm_syncobj_timeline_create(int drm_fd); +/** + * Reference a synchronization timeline. + */ +struct wlr_drm_syncobj_timeline *wlr_drm_syncobj_timeline_ref(struct wlr_drm_syncobj_timeline *timeline); +/** + * Unreference a synchronization timeline. + */ +void wlr_drm_syncobj_timeline_unref(struct wlr_drm_syncobj_timeline *timeline); +/** + * Export a timeline point as a sync_file FD. + * + * The returned sync_file will be signalled when the provided point is + * signalled on the timeline. + * + * This allows inter-operation with other APIs which don't support drm_syncobj + * yet. The synchronization point needs to have already materialized: + * wait-before-signal is not supported. + */ +int wlr_drm_syncobj_timeline_export_sync_file(struct wlr_drm_syncobj_timeline *timeline, + uint64_t src_point); +/** + * Import a timeline point from a sync_file FD. + * + * The provided timeline point will be signalled when the provided sync_file is. + * + * This allows inter-operation with other APIs which don't support drm_syncobj + * yet. + */ +bool wlr_drm_syncobj_timeline_import_sync_file(struct wlr_drm_syncobj_timeline *timeline, + uint64_t dst_point, int sync_file_fd); + +#endif diff --git a/render/drm_syncobj.c b/render/drm_syncobj.c new file mode 100644 index 000000000..974b1b40d --- /dev/null +++ b/render/drm_syncobj.c @@ -0,0 +1,98 @@ +#include +#include +#include +#include +#include + +struct wlr_drm_syncobj_timeline *wlr_drm_syncobj_timeline_create(int drm_fd) { + struct wlr_drm_syncobj_timeline *timeline = calloc(1, sizeof(*timeline)); + if (timeline == NULL) { + return NULL; + } + timeline->drm_fd = drm_fd; + timeline->n_refs = 1; + + if (drmSyncobjCreate(drm_fd, 0, &timeline->handle) != 0) { + wlr_log_errno(WLR_ERROR, "drmSyncobjCreate failed"); + free(timeline); + return NULL; + } + + return timeline; +} + +struct wlr_drm_syncobj_timeline *wlr_drm_syncobj_timeline_ref(struct wlr_drm_syncobj_timeline *timeline) { + timeline->n_refs++; + return timeline; +} + +void wlr_drm_syncobj_timeline_unref(struct wlr_drm_syncobj_timeline *timeline) { + if (timeline == NULL) { + return; + } + + assert(timeline->n_refs > 0); + timeline->n_refs--; + if (timeline->n_refs > 0) { + return; + } + + drmSyncobjDestroy(timeline->drm_fd, timeline->handle); + free(timeline); +} + +int wlr_drm_syncobj_timeline_export_sync_file(struct wlr_drm_syncobj_timeline *timeline, + uint64_t src_point) { + int sync_file_fd = -1; + + uint32_t syncobj_handle; + if (drmSyncobjCreate(timeline->drm_fd, 0, &syncobj_handle) != 0) { + wlr_log_errno(WLR_ERROR, "drmSyncobjCreate failed"); + return -1; + } + + if (drmSyncobjTransfer(timeline->drm_fd, syncobj_handle, 0, + timeline->handle, src_point, 0) != 0) { + wlr_log_errno(WLR_ERROR, "drmSyncobjTransfer failed"); + goto out; + } + + if (drmSyncobjExportSyncFile(timeline->drm_fd, + syncobj_handle, &sync_file_fd) != 0) { + wlr_log_errno(WLR_ERROR, "drmSyncobjExportSyncFile failed"); + goto out; + } + +out: + drmSyncobjDestroy(timeline->drm_fd, syncobj_handle); + return sync_file_fd; +} + +bool wlr_drm_syncobj_timeline_import_sync_file(struct wlr_drm_syncobj_timeline *timeline, + uint64_t dst_point, int sync_file_fd) { + bool ok = false; + + uint32_t syncobj_handle; + if (drmSyncobjCreate(timeline->drm_fd, 0, &syncobj_handle) != 0) { + wlr_log_errno(WLR_ERROR, "drmSyncobjCreate failed"); + return -1; + } + + if (drmSyncobjImportSyncFile(timeline->drm_fd, syncobj_handle, + sync_file_fd) != 0) { + wlr_log_errno(WLR_ERROR, "drmSyncobjImportSyncFile failed"); + goto out; + } + + if (drmSyncobjTransfer(timeline->drm_fd, timeline->handle, dst_point, + syncobj_handle, 0, 0) != 0) { + wlr_log_errno(WLR_ERROR, "drmSyncobjTransfer failed"); + goto out; + } + + ok = true; + +out: + drmSyncobjDestroy(timeline->drm_fd, syncobj_handle); + return ok; +} diff --git a/render/meson.build b/render/meson.build index f3e497dd0..6cae57512 100644 --- a/render/meson.build +++ b/render/meson.build @@ -9,6 +9,7 @@ wlr_files += files( 'color.c', 'dmabuf.c', 'drm_format_set.c', + 'drm_syncobj.c', 'pass.c', 'pixel_format.c', 'swapchain.c',