diff --git a/include/render/drm_syncobj_merger.h b/include/render/drm_syncobj_merger.h index c55c87314..397c5ce80 100644 --- a/include/render/drm_syncobj_merger.h +++ b/include/render/drm_syncobj_merger.h @@ -3,6 +3,8 @@ #include +struct wlr_buffer; + /** * Accumulate timeline points, to have a destination timeline point be * signalled when all inputs are @@ -41,4 +43,22 @@ bool wlr_drm_syncobj_merger_add(struct wlr_drm_syncobj_merger *merger, struct wlr_drm_syncobj_timeline *dst_timeline, uint64_t dst_point, struct wl_event_loop *loop); +/** + * Add a new sync file to wait for. + * + * Ownership of fd is transferred to the merger. + */ +bool wlr_drm_syncobj_merger_add_sync_file(struct wlr_drm_syncobj_merger *merger, + int fd); + +/** + * Add a new DMA-BUF release to wait for. + * + * Waits for write access. + * If the platform does not support DMA-BUF<->sync file interop, the supplied + * event_loop is used to schedule a wait. + */ +bool wlr_drm_syncobj_merger_add_dmabuf(struct wlr_drm_syncobj_merger *merger, + struct wlr_buffer *buffer, struct wl_event_loop *event_loop); + #endif diff --git a/include/wlr/types/wlr_ext_image_copy_capture_v1.h b/include/wlr/types/wlr_ext_image_copy_capture_v1.h index 0b02b9808..7edd9270f 100644 --- a/include/wlr/types/wlr_ext_image_copy_capture_v1.h +++ b/include/wlr/types/wlr_ext_image_copy_capture_v1.h @@ -15,6 +15,7 @@ #include struct wlr_renderer; +struct wlr_drm_syncobj_timeline; struct wlr_ext_image_copy_capture_manager_v1 { struct wl_global *global; @@ -82,6 +83,7 @@ void wlr_ext_image_copy_capture_frame_v1_fail(struct wlr_ext_image_copy_capture_ * Copy a struct wlr_buffer into the client-provided buffer for the frame. */ bool wlr_ext_image_copy_capture_frame_v1_copy_buffer(struct wlr_ext_image_copy_capture_frame_v1 *frame, - struct wlr_buffer *src, struct wlr_renderer *renderer); + struct wlr_buffer *src, struct wlr_renderer *renderer, + struct wlr_drm_syncobj_timeline *wait_timeline, uint64_t wait_point); #endif diff --git a/include/wlr/types/wlr_linux_drm_syncobj_v1.h b/include/wlr/types/wlr_linux_drm_syncobj_v1.h index 7fd55ec2e..dfcbc4d43 100644 --- a/include/wlr/types/wlr_linux_drm_syncobj_v1.h +++ b/include/wlr/types/wlr_linux_drm_syncobj_v1.h @@ -74,4 +74,21 @@ bool wlr_linux_drm_syncobj_v1_state_add_release_point( struct wlr_drm_syncobj_timeline *release_timeline, uint64_t release_point, struct wl_event_loop *event_loop); +/** + * Register the DMA-BUF release of a buffer for buffer usage. + * Non-dmabuf buffers are considered to be immediately available (no wait). + * + * This function may be called multiple times for the same commit. The client's + * release point will be signalled when all registered points are signalled, and + * a new buffer has been committed. + * + * Because the platform may not support DMA-BUF fence merges, a wl_event_loop + * must be supplied to schedule a wait internally, if needed + * + * Waits for write access + */ +bool wlr_linux_drm_syncobj_v1_state_add_release_from_implicit_sync( + struct wlr_linux_drm_syncobj_surface_v1_state *state, + struct wlr_buffer *buffer, struct wl_event_loop *event_loop); + #endif diff --git a/render/drm_syncobj_merger.c b/render/drm_syncobj_merger.c index d50d28c28..0cfdfcc96 100644 --- a/render/drm_syncobj_merger.c +++ b/render/drm_syncobj_merger.c @@ -4,8 +4,10 @@ #include #include #include +#include #include #include +#include "render/dmabuf.h" #include "render/drm_syncobj_merger.h" #include "config.h" @@ -79,14 +81,7 @@ void wlr_drm_syncobj_merger_unref(struct wlr_drm_syncobj_merger *merger) { static bool merger_add_exportable(struct wlr_drm_syncobj_merger *merger, struct wlr_drm_syncobj_timeline *src_timeline, uint64_t src_point) { int new_sync = wlr_drm_syncobj_timeline_export_sync_file(src_timeline, src_point); - if (merger->sync_fd != -1) { - int fd2 = new_sync; - new_sync = sync_file_merge(merger->sync_fd, fd2); - close(fd2); - close(merger->sync_fd); - } - merger->sync_fd = new_sync; - return true; + return wlr_drm_syncobj_merger_add_sync_file(merger, new_sync); } struct export_waiter { @@ -131,3 +126,69 @@ bool wlr_drm_syncobj_merger_add(struct wlr_drm_syncobj_merger *merger, merger->n_ref++; return true; } + +bool wlr_drm_syncobj_merger_add_sync_file(struct wlr_drm_syncobj_merger *merger, + int fd) { + int new_sync = fd; + if (merger->sync_fd != -1) { + new_sync = sync_file_merge(merger->sync_fd, fd); + close(fd); + close(merger->sync_fd); + } + merger->sync_fd = new_sync; + return merger->sync_fd != -1; +} + +struct poll_waiter { + struct wl_event_source *event_source; + struct wlr_drm_syncobj_merger *merger; +}; + +static int poll_waiter_handle_done(int fd, uint32_t mask, void *data) { + struct poll_waiter *waiter = data; + wlr_drm_syncobj_merger_unref(waiter->merger); + wl_event_source_remove(waiter->event_source); + free(waiter); + return 0; +} + +bool wlr_drm_syncobj_merger_add_dmabuf(struct wlr_drm_syncobj_merger *merger, + struct wlr_buffer *buffer, struct wl_event_loop *event_loop) { + struct wlr_dmabuf_attributes dmabuf_attributes; + if (!wlr_buffer_get_dmabuf(buffer, &dmabuf_attributes)) { + return true; + } + + bool res = true; + for (int i = 0; i < dmabuf_attributes.n_planes; ++i) { + int sync_fd = dmabuf_export_sync_file(dmabuf_attributes.fd[i], DMA_BUF_SYNC_WRITE); + if (sync_fd == -1) { + res = false; + break; + } + if (!wlr_drm_syncobj_merger_add_sync_file(merger, sync_fd)) { + return false; + } + } + + if (res) { + return true; + } + + uint32_t mask = WL_EVENT_ERROR | WL_EVENT_HANGUP | WL_EVENT_WRITABLE; + for (int i = 0; i < dmabuf_attributes.n_planes; ++i) { + struct poll_waiter *waiter = calloc(1, sizeof(*waiter)); + if (waiter == NULL) { + return false; + } + waiter->merger = wlr_drm_syncobj_merger_ref(merger); + waiter->event_source = wl_event_loop_add_fd(event_loop, + dmabuf_attributes.fd[i], mask, poll_waiter_handle_done, waiter); + if (waiter->event_source == NULL) { + wlr_drm_syncobj_merger_unref(waiter->merger); + free(waiter); + return false; + } + } + return true; +} diff --git a/types/ext_image_capture_source_v1/output.c b/types/ext_image_capture_source_v1/output.c index 0e3a57823..53589faa3 100644 --- a/types/ext_image_capture_source_v1/output.c +++ b/types/ext_image_capture_source_v1/output.c @@ -41,6 +41,8 @@ struct wlr_ext_output_image_capture_source_v1_frame_event { struct wlr_ext_image_capture_source_v1_frame_event base; struct wlr_buffer *buffer; struct timespec when; + struct wlr_drm_syncobj_timeline *wait_timeline; + uint64_t wait_point; }; static void output_source_start(struct wlr_ext_image_capture_source_v1 *base, @@ -86,7 +88,8 @@ static void output_source_copy_frame(struct wlr_ext_image_capture_source_v1 *bas wl_container_of(base_event, event, base); if (wlr_ext_image_copy_capture_frame_v1_copy_buffer(frame, - event->buffer, source->output->renderer)) { + event->buffer, source->output->renderer, + event->wait_timeline, event->wait_point)) { wlr_ext_image_copy_capture_frame_v1_ready(frame, source->output->transform, &event->when); } @@ -152,6 +155,10 @@ static void source_handle_output_commit(struct wl_listener *listener, .buffer = buffer, .when = event->when, // TODO: predict next presentation time instead }; + if (event->state->committed & WLR_OUTPUT_STATE_WAIT_TIMELINE) { + frame_event.wait_timeline = event->state->wait_timeline; + frame_event.wait_point = event->state->wait_point; + } wl_signal_emit_mutable(&source->base.events.frame, &frame_event); pixman_region32_fini(&full_damage); @@ -279,6 +286,8 @@ static void output_cursor_source_copy_frame(struct wlr_ext_image_capture_source_ struct wlr_ext_image_copy_capture_frame_v1 *frame, struct wlr_ext_image_capture_source_v1_frame_event *base_event) { struct output_cursor_source *cursor_source = wl_container_of(base, cursor_source, base); + struct wlr_ext_output_image_capture_source_v1_frame_event *event = + wl_container_of(base_event, event, base); struct wlr_buffer *src_buffer = cursor_source->output->cursor_front_buffer; if (src_buffer == NULL) { @@ -287,7 +296,8 @@ static void output_cursor_source_copy_frame(struct wlr_ext_image_capture_source_ } if (!wlr_ext_image_copy_capture_frame_v1_copy_buffer(frame, - src_buffer, cursor_source->output->renderer)) { + src_buffer, cursor_source->output->renderer, + event->wait_timeline, event->wait_point)) { return; } diff --git a/types/ext_image_capture_source_v1/scene.c b/types/ext_image_capture_source_v1/scene.c index 7d4b8928a..989fc4f31 100644 --- a/types/ext_image_capture_source_v1/scene.c +++ b/types/ext_image_capture_source_v1/scene.c @@ -7,6 +7,8 @@ #include #include +#include "render/dmabuf.h" +#include "render/drm_syncobj_merger.h" #include "types/wlr_output.h" #include "types/wlr_scene.h" @@ -30,6 +32,9 @@ struct scene_node_source_frame_event { struct wlr_ext_image_capture_source_v1_frame_event base; struct wlr_buffer *buffer; struct timespec when; + struct wlr_drm_syncobj_timeline *wait_timeline; + uint64_t wait_point; + struct wlr_drm_syncobj_merger *release_merger; }; static size_t last_output_num = 0; @@ -149,9 +154,14 @@ static void source_copy_frame(struct wlr_ext_image_capture_source_v1 *base, struct scene_node_source_frame_event *event = wl_container_of(base_event, event, base); if (wlr_ext_image_copy_capture_frame_v1_copy_buffer(frame, - event->buffer, source->output.renderer)) { + event->buffer, source->output.renderer, + event->wait_timeline, event->wait_point)) { wlr_ext_image_copy_capture_frame_v1_ready(frame, source->output.transform, &event->when); + if (event->release_merger) { + wlr_drm_syncobj_merger_add_dmabuf(event->release_merger, frame->buffer, + source->output.event_loop); + } } } @@ -162,7 +172,14 @@ static const struct wlr_ext_image_capture_source_v1_interface source_impl = { .copy_frame = source_copy_frame, }; -static const struct wlr_backend_impl backend_impl = {0}; +static int source_backend_get_drm_fd(struct wlr_backend *backend) { + struct scene_node_source *source = wl_container_of(backend, source, backend); + return wlr_renderer_get_drm_fd(source->output.renderer); +} + +static const struct wlr_backend_impl backend_impl = { + .get_drm_fd = source_backend_get_drm_fd +}; static void source_update_buffer_constraints(struct scene_node_source *source, const struct wlr_output_state *state) { @@ -180,6 +197,8 @@ static bool output_test(struct wlr_output *output, const struct wlr_output_state uint32_t supported = WLR_OUTPUT_STATE_BACKEND_OPTIONAL | WLR_OUTPUT_STATE_BUFFER | + WLR_OUTPUT_STATE_WAIT_TIMELINE | + WLR_OUTPUT_STATE_SIGNAL_TIMELINE | WLR_OUTPUT_STATE_ENABLED | WLR_OUTPUT_STATE_MODE; if ((state->committed & ~supported) != 0) { @@ -242,7 +261,16 @@ static bool output_commit(struct wlr_output *output, const struct wlr_output_sta .buffer = buffer, .when = now, }; + if (state->committed & WLR_OUTPUT_STATE_WAIT_TIMELINE) { + frame_event.wait_timeline = state->wait_timeline; + frame_event.wait_point = state->wait_point; + } + if (state->committed & WLR_OUTPUT_STATE_SIGNAL_TIMELINE) { + frame_event.release_merger = wlr_drm_syncobj_merger_create( + state->signal_timeline, state->signal_point); + } wl_signal_emit_mutable(&source->base.events.frame, &frame_event.base); + wlr_drm_syncobj_merger_unref(frame_event.release_merger); pixman_region32_fini(&full_damage); @@ -311,6 +339,7 @@ struct wlr_ext_image_capture_source_v1 *wlr_ext_image_capture_source_v1_create_w wlr_backend_init(&source->backend, &backend_impl); source->backend.buffer_caps = WLR_BUFFER_CAP_DMABUF | WLR_BUFFER_CAP_SHM; + source->backend.features.timeline = true; wlr_output_init(&source->output, &source->backend, &output_impl, event_loop, NULL); diff --git a/types/wlr_ext_image_copy_capture_v1.c b/types/wlr_ext_image_copy_capture_v1.c index 2e969b4a9..9b0f4856d 100644 --- a/types/wlr_ext_image_copy_capture_v1.c +++ b/types/wlr_ext_image_copy_capture_v1.c @@ -105,9 +105,9 @@ void wlr_ext_image_copy_capture_frame_v1_ready(struct wlr_ext_image_copy_capture frame_destroy(frame); } -static bool copy_dmabuf(struct wlr_buffer *dst, - struct wlr_buffer *src, struct wlr_renderer *renderer, - const pixman_region32_t *clip) { +static bool copy_dmabuf(struct wlr_buffer *dst, struct wlr_buffer *src, + struct wlr_renderer *renderer, const pixman_region32_t *clip, + struct wlr_drm_syncobj_timeline *wait_timeline, uint64_t wait_point) { struct wlr_texture *texture = wlr_texture_from_buffer(renderer, src); if (texture == NULL) { return false; @@ -123,6 +123,8 @@ static bool copy_dmabuf(struct wlr_buffer *dst, .texture = texture, .clip = clip, .blend_mode = WLR_RENDER_BLEND_MODE_NONE, + .wait_timeline = wait_timeline, + .wait_point = wait_point, }); ok = wlr_render_pass_submit(pass); @@ -133,7 +135,8 @@ out: } static bool copy_shm(void *data, uint32_t format, size_t stride, - struct wlr_buffer *src, struct wlr_renderer *renderer) { + struct wlr_buffer *src, struct wlr_renderer *renderer, + struct wlr_drm_syncobj_timeline *wait_timeline, uint64_t wait_point) { // TODO: bypass renderer if source buffer supports data ptr access struct wlr_texture *texture = wlr_texture_from_buffer(renderer, src); if (!texture) { @@ -145,6 +148,8 @@ static bool copy_shm(void *data, uint32_t format, size_t stride, .data = data, .format = format, .stride = stride, + .wait_timeline = wait_timeline, + .wait_point = wait_point, }); wlr_texture_destroy(texture); @@ -153,7 +158,8 @@ static bool copy_shm(void *data, uint32_t format, size_t stride, } bool wlr_ext_image_copy_capture_frame_v1_copy_buffer(struct wlr_ext_image_copy_capture_frame_v1 *frame, - struct wlr_buffer *src, struct wlr_renderer *renderer) { + struct wlr_buffer *src, struct wlr_renderer *renderer, + struct wlr_drm_syncobj_timeline *wait_timeline, uint64_t wait_point) { struct wlr_buffer *dst = frame->buffer; if (src->width != dst->width || src->height != dst->height) { @@ -174,7 +180,7 @@ bool wlr_ext_image_copy_capture_frame_v1_copy_buffer(struct wlr_ext_image_copy_c ok = false; failure_reason = EXT_IMAGE_COPY_CAPTURE_FRAME_V1_FAILURE_REASON_BUFFER_CONSTRAINTS; } else { - ok = copy_dmabuf(dst, src, renderer, &frame->buffer_damage); + ok = copy_dmabuf(dst, src, renderer, &frame->buffer_damage, wait_timeline, wait_point); } } else if (wlr_buffer_begin_data_ptr_access(dst, WLR_BUFFER_DATA_PTR_ACCESS_WRITE, &data, &format, &stride)) { @@ -182,7 +188,7 @@ bool wlr_ext_image_copy_capture_frame_v1_copy_buffer(struct wlr_ext_image_copy_c ok = false; failure_reason = EXT_IMAGE_COPY_CAPTURE_FRAME_V1_FAILURE_REASON_BUFFER_CONSTRAINTS; } else { - ok = copy_shm(data, format, stride, src, renderer); + ok = copy_shm(data, format, stride, src, renderer, wait_timeline, wait_point); } wlr_buffer_end_data_ptr_access(dst); } diff --git a/types/wlr_linux_drm_syncobj_v1.c b/types/wlr_linux_drm_syncobj_v1.c index 53fc2fd43..b9865dcce 100644 --- a/types/wlr_linux_drm_syncobj_v1.c +++ b/types/wlr_linux_drm_syncobj_v1.c @@ -12,6 +12,7 @@ #include #include "config.h" #include "linux-drm-syncobj-v1-protocol.h" +#include "render/dmabuf.h" #include "render/drm_syncobj_merger.h" #define LINUX_DRM_SYNCOBJ_V1_VERSION 1 @@ -540,3 +541,12 @@ bool wlr_linux_drm_syncobj_v1_state_add_release_point( return wlr_drm_syncobj_merger_add(state->release_merger, release_timeline, release_point, event_loop); } + +bool wlr_linux_drm_syncobj_v1_state_add_release_from_implicit_sync( + struct wlr_linux_drm_syncobj_surface_v1_state *state, + struct wlr_buffer *buffer, struct wl_event_loop *event_loop) { + if (state->release_merger != NULL) { + return true; + } + return wlr_drm_syncobj_merger_add_dmabuf(state->release_merger, buffer, event_loop); +}