From bfc616fc7ed74855acb14440594548d5cc3e291d Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Wed, 20 Oct 2021 18:47:46 +0200 Subject: [PATCH] render/timeline: introduce wlr_timeline wlr_timeline is a synchronization primitive based on drm_syncobj timelines. They are heavily inspired from Vulkan timeline semaphores [1]. [1]: https://www.khronos.org/blog/vulkan-timeline-semaphores --- include/wlr/render/timeline.h | 60 ++++++++++++++++++++++++ render/meson.build | 1 + render/timeline.c | 86 +++++++++++++++++++++++++++++++++++ 3 files changed, 147 insertions(+) create mode 100644 include/wlr/render/timeline.h create mode 100644 render/timeline.c diff --git a/include/wlr/render/timeline.h b/include/wlr/render/timeline.h new file mode 100644 index 000000000..80705cb56 --- /dev/null +++ b/include/wlr/render/timeline.h @@ -0,0 +1,60 @@ +#ifndef WLR_RENDER_TIMELINE_H +#define WLR_RENDER_TIMELINE_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_render_timeline; + +/** + * Create a new synchronization timeline. + */ +struct wlr_render_timeline *wlr_render_timeline_create(int drm_fd); +/** + * Destroy a synchronization timeline. + */ +void wlr_render_timeline_destroy(struct wlr_render_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_render_timeline_export_sync_file(struct wlr_render_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_render_timeline_import_sync_file(struct wlr_render_timeline *timeline, + uint64_t dst_point, int sync_file_fd); + +#endif diff --git a/render/meson.build b/render/meson.build index 2bdcced8f..63c0ee439 100644 --- a/render/meson.build +++ b/render/meson.build @@ -10,6 +10,7 @@ wlr_files += files( 'drm_format_set.c', 'pixel_format.c', 'swapchain.c', + 'timeline.c', 'wlr_renderer.c', 'wlr_texture.c', ) diff --git a/render/timeline.c b/render/timeline.c new file mode 100644 index 000000000..6acf3e8ee --- /dev/null +++ b/render/timeline.c @@ -0,0 +1,86 @@ +#include +#include +#include +#include + +struct wlr_render_timeline { + int drm_fd; + uint32_t handle; +}; + +struct wlr_render_timeline *wlr_render_timeline_create(int drm_fd) { + struct wlr_render_timeline *timeline = calloc(1, sizeof(*timeline)); + if (timeline == NULL) { + return NULL; + } + timeline->drm_fd = drm_fd; + + if (drmSyncobjCreate(drm_fd, 0, &timeline->handle) != 0) { + wlr_log_errno(WLR_ERROR, "drmSyncobjCreate failed"); + free(timeline); + return NULL; + } + + return timeline; +} + +void wlr_render_timeline_destroy(struct wlr_render_timeline *timeline) { + drmSyncobjDestroy(timeline->drm_fd, timeline->handle); + free(timeline); +} + +int wlr_render_timeline_export_sync_file(struct wlr_render_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_render_timeline_import_sync_file(struct wlr_render_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; +}