From 85be315268f036080a3c0774f3992dafe346efe4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20Poisot?= Date: Fri, 27 Mar 2026 20:06:57 +0000 Subject: [PATCH 1/3] ext_image_capture_source_v1/output: wait for explicit sync Synchronize from the scene render to the copy to capture client. Buffer return is still covered by implicit sync. We know this is the scene's render buffer, direct scanout is disallowed for the output capture source. --- .../wlr/types/wlr_ext_image_copy_capture_v1.h | 4 +++- types/ext_image_capture_source_v1/output.c | 14 +++++++++++-- types/ext_image_capture_source_v1/scene.c | 2 +- types/wlr_ext_image_copy_capture_v1.c | 20 ++++++++++++------- 4 files changed, 29 insertions(+), 11 deletions(-) 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/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..07a1f52e7 100644 --- a/types/ext_image_capture_source_v1/scene.c +++ b/types/ext_image_capture_source_v1/scene.c @@ -149,7 +149,7 @@ 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, NULL, 0)) { wlr_ext_image_copy_capture_frame_v1_ready(frame, source->output.transform, &event->when); } 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); } From 55ab58256355d1909077d6c46551ce16ce5e9c8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20Poisot?= Date: Sat, 28 Mar 2026 09:58:12 +0000 Subject: [PATCH 2/3] ext_image_capture_source_v1/scene: use explicit sync Allow and handle timelines on the virtual backend. --- types/ext_image_capture_source_v1/scene.c | 33 +++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/types/ext_image_capture_source_v1/scene.c b/types/ext_image_capture_source_v1/scene.c index 07a1f52e7..79bc9ad01 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, NULL, 0)) { + 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, event->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); From ee980b79a350aa54f195a9c949ba4913199769e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20Poisot?= Date: Wed, 20 May 2026 19:57:02 +0000 Subject: [PATCH 3/3] ext_image_copy_capture: explicit sync for frame copy Keep a drm timeline in `wlr_ext_image_copy_capture_session_v1`, allowing asynchronous operation for `wlr_ext_image_copy_capture_frame_v1_copy_buffer()` --- .../wlr/types/wlr_ext_image_copy_capture_v1.h | 25 +++++- types/ext_image_capture_source_v1/output.c | 26 ++++-- types/ext_image_capture_source_v1/scene.c | 20 +++-- types/wlr_ext_image_copy_capture_v1.c | 80 ++++++++++++++++++- 4 files changed, 135 insertions(+), 16 deletions(-) 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 7edd9270f..8ab3fd7e6 100644 --- a/include/wlr/types/wlr_ext_image_copy_capture_v1.h +++ b/include/wlr/types/wlr_ext_image_copy_capture_v1.h @@ -12,6 +12,7 @@ #include #include #include +#include #include struct wlr_renderer; @@ -44,6 +45,8 @@ struct wlr_ext_image_copy_capture_session_v1 { struct wl_listener source_frame; pixman_region32_t damage; + struct wlr_drm_syncobj_timeline *copy_timeline; + uint64_t copy_point; } WLR_PRIVATE; }; @@ -59,6 +62,10 @@ struct wlr_ext_image_copy_capture_frame_v1 { struct { struct wlr_ext_image_copy_capture_session_v1 *session; + enum wl_output_transform pending_transform; + struct timespec pending_presentation_time; + bool copy_waiter_initialized; + struct wlr_drm_syncobj_timeline_waiter copy_waiter; } WLR_PRIVATE; }; @@ -72,6 +79,17 @@ struct wlr_ext_image_copy_capture_manager_v1 *wlr_ext_image_copy_capture_manager */ void wlr_ext_image_copy_capture_frame_v1_ready(struct wlr_ext_image_copy_capture_frame_v1 *frame, enum wl_output_transform transform, const struct timespec *presentation_time); + +/** + * Notify the client that the frame is ready, when timeline point is signalled. + * + * This function causes the frame destruction, and may destroy it synchronously. + */ +bool wlr_ext_image_copy_capture_frame_v1_ready_deferred( + struct wlr_ext_image_copy_capture_frame_v1 *frame, + enum wl_output_transform transform, const struct timespec *presentation_time, + struct wlr_drm_syncobj_timeline *timeline, uint64_t point); + /** * Notify the client that the frame has failed. * @@ -81,9 +99,14 @@ void wlr_ext_image_copy_capture_frame_v1_fail(struct wlr_ext_image_copy_capture_ enum ext_image_copy_capture_frame_v1_failure_reason reason); /** * Copy a struct wlr_buffer into the client-provided buffer for the frame. + * + * If the caller obtains a timeline point through `out_copy_timeline` and + * `out_copy_timeline`, it must wait for it to signal before sending the + * "frame ready" event to the capture client */ 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_drm_syncobj_timeline *wait_timeline, uint64_t wait_point); + struct wlr_drm_syncobj_timeline *wait_timeline, uint64_t wait_point, + struct wlr_drm_syncobj_timeline **out_copy_timeline, uint64_t *out_copy_point); #endif diff --git a/types/ext_image_capture_source_v1/output.c b/types/ext_image_capture_source_v1/output.c index 53589faa3..2664e74de 100644 --- a/types/ext_image_capture_source_v1/output.c +++ b/types/ext_image_capture_source_v1/output.c @@ -87,11 +87,19 @@ static void output_source_copy_frame(struct wlr_ext_image_capture_source_v1 *bas struct wlr_ext_output_image_capture_source_v1_frame_event *event = wl_container_of(base_event, event, base); + struct wlr_drm_syncobj_timeline *copy_timeline; + uint64_t copy_point; if (wlr_ext_image_copy_capture_frame_v1_copy_buffer(frame, 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); + event->wait_timeline, event->wait_point, ©_timeline, ©_point)) { + if (copy_timeline != NULL) { + wlr_ext_image_copy_capture_frame_v1_ready_deferred(frame, + source->output->transform, &event->when, copy_timeline, copy_point); + wlr_drm_syncobj_timeline_unref(copy_timeline); + } else { + wlr_ext_image_copy_capture_frame_v1_ready(frame, + source->output->transform, &event->when); + } } } @@ -295,17 +303,25 @@ static void output_cursor_source_copy_frame(struct wlr_ext_image_capture_source_ return; } + struct wlr_drm_syncobj_timeline *copy_timeline; + uint64_t copy_point; if (!wlr_ext_image_copy_capture_frame_v1_copy_buffer(frame, src_buffer, cursor_source->output->renderer, - event->wait_timeline, event->wait_point)) { + event->wait_timeline, event->wait_point, ©_timeline, ©_point)) { return; } struct timespec now; clock_gettime(CLOCK_MONOTONIC, &now); - wlr_ext_image_copy_capture_frame_v1_ready(frame, + if (copy_timeline != NULL) { + wlr_ext_image_copy_capture_frame_v1_ready_deferred(frame, + cursor_source->output->transform, &now, copy_timeline, copy_point); + wlr_drm_syncobj_timeline_unref(copy_timeline); + } else { + wlr_ext_image_copy_capture_frame_v1_ready(frame, cursor_source->output->transform, &now); + } } static const struct wlr_ext_image_capture_source_v1_interface output_cursor_source_impl = { diff --git a/types/ext_image_capture_source_v1/scene.c b/types/ext_image_capture_source_v1/scene.c index 79bc9ad01..5ee3e9a58 100644 --- a/types/ext_image_capture_source_v1/scene.c +++ b/types/ext_image_capture_source_v1/scene.c @@ -153,14 +153,22 @@ static void source_copy_frame(struct wlr_ext_image_capture_source_v1 *base, struct scene_node_source *source = wl_container_of(base, source, base); struct scene_node_source_frame_event *event = wl_container_of(base_event, event, base); + struct wlr_drm_syncobj_timeline *copy_timeline; + uint64_t copy_point; if (wlr_ext_image_copy_capture_frame_v1_copy_buffer(frame, 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, event->buffer, - source->output.event_loop); + event->wait_timeline, event->wait_point, ©_timeline, ©_point)) { + if (copy_timeline != NULL) { + if (event->release_merger != NULL) { + wlr_drm_syncobj_merger_add(event->release_merger, copy_timeline, copy_point, + source->output.event_loop); + } + wlr_ext_image_copy_capture_frame_v1_ready_deferred(frame, + source->output.transform, &event->when, copy_timeline, copy_point); + wlr_drm_syncobj_timeline_unref(copy_timeline); + } else { + wlr_ext_image_copy_capture_frame_v1_ready(frame, + source->output.transform, &event->when); } } } diff --git a/types/wlr_ext_image_copy_capture_v1.c b/types/wlr_ext_image_copy_capture_v1.c index 9b0f4856d..8cfd7852b 100644 --- a/types/wlr_ext_image_copy_capture_v1.c +++ b/types/wlr_ext_image_copy_capture_v1.c @@ -74,6 +74,9 @@ static void frame_destroy(struct wlr_ext_image_copy_capture_frame_v1 *frame) { if (frame->session->frame == frame) { frame->session->frame = NULL; } + if (frame->copy_waiter_initialized) { + wlr_drm_syncobj_timeline_waiter_finish(&frame->copy_waiter); + } free(frame); } @@ -107,14 +110,19 @@ void wlr_ext_image_copy_capture_frame_v1_ready(struct wlr_ext_image_copy_capture 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_drm_syncobj_timeline *wait_timeline, uint64_t wait_point, + struct wlr_drm_syncobj_timeline *signal_timeline, uint64_t signal_point) { struct wlr_texture *texture = wlr_texture_from_buffer(renderer, src); if (texture == NULL) { return false; } bool ok = false; - struct wlr_render_pass *pass = wlr_renderer_begin_buffer_pass(renderer, dst, NULL); + struct wlr_buffer_pass_options options = { + .signal_timeline = signal_timeline, + .signal_point = signal_point, + }; + struct wlr_render_pass *pass = wlr_renderer_begin_buffer_pass(renderer, dst, &options); if (!pass) { goto out; } @@ -159,8 +167,15 @@ 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_drm_syncobj_timeline *wait_timeline, uint64_t wait_point) { + struct wlr_drm_syncobj_timeline *wait_timeline, uint64_t wait_point, + struct wlr_drm_syncobj_timeline **out_copy_timeline, uint64_t *out_copy_point) { struct wlr_buffer *dst = frame->buffer; + if (out_copy_timeline) { + *out_copy_timeline = NULL; + } + if (out_copy_point) { + *out_copy_point = 0; + } if (src->width != dst->width || src->height != dst->height) { wlr_ext_image_copy_capture_frame_v1_fail(frame, @@ -168,6 +183,20 @@ bool wlr_ext_image_copy_capture_frame_v1_copy_buffer(struct wlr_ext_image_copy_c return false; } + struct wlr_drm_syncobj_timeline *copy_timeline = frame->session->copy_timeline; + if (copy_timeline == NULL && renderer->features.timeline) { + int drm_fd = wlr_renderer_get_drm_fd(renderer); + copy_timeline = wlr_drm_syncobj_timeline_create(drm_fd); + if (copy_timeline == NULL) { + wlr_ext_image_copy_capture_frame_v1_fail(frame, + EXT_IMAGE_COPY_CAPTURE_FRAME_V1_FAILURE_REASON_UNKNOWN); + return false; + } + frame->session->copy_timeline = copy_timeline; + } + frame->session->copy_point++; + uint64_t copy_point = frame->session->copy_point; + bool ok = false; enum ext_image_copy_capture_frame_v1_failure_reason failure_reason = EXT_IMAGE_COPY_CAPTURE_FRAME_V1_FAILURE_REASON_UNKNOWN; @@ -180,7 +209,18 @@ 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, wait_timeline, wait_point); + if (out_copy_timeline == NULL) { + copy_timeline = NULL; + copy_point = 0; + } + ok = copy_dmabuf(dst, src, renderer, &frame->buffer_damage, + wait_timeline, wait_point, copy_timeline, copy_point); + if (ok && copy_timeline != NULL) { + *out_copy_timeline = wlr_drm_syncobj_timeline_ref(frame->session->copy_timeline); + } + if (ok && out_copy_point != NULL) { + *out_copy_point = copy_point; + } } } else if (wlr_buffer_begin_data_ptr_access(dst, WLR_BUFFER_DATA_PTR_ACCESS_WRITE, &data, &format, &stride)) { @@ -206,6 +246,37 @@ void wlr_ext_image_copy_capture_frame_v1_fail(struct wlr_ext_image_copy_capture_ frame_destroy(frame); } +static void frame_handle_copy_done(struct wlr_drm_syncobj_timeline_waiter *waiter) { + struct wlr_ext_image_copy_capture_frame_v1 *frame = wl_container_of(waiter, frame, copy_waiter); + wlr_ext_image_copy_capture_frame_v1_ready(frame, frame->pending_transform, + &frame->pending_presentation_time); +} + +bool wlr_ext_image_copy_capture_frame_v1_ready_deferred( + struct wlr_ext_image_copy_capture_frame_v1 *frame, + enum wl_output_transform transform, const struct timespec *presentation_time, + struct wlr_drm_syncobj_timeline *timeline, uint64_t point) { + assert(!frame->copy_waiter_initialized); + + frame->pending_transform = transform; + frame->pending_presentation_time = *presentation_time; + struct wl_display *display = wl_client_get_display(frame->resource->client); + struct wl_event_loop *event_loop = wl_display_get_event_loop(display); + bool ok = wlr_drm_syncobj_timeline_waiter_init(&frame->copy_waiter, timeline, point, + 0, event_loop, frame_handle_copy_done); + frame->copy_waiter_initialized = ok; + if (ok) { + if (frame->session->frame == frame) { + frame->session->frame = NULL; + } + } else { + wlr_ext_image_copy_capture_frame_v1_fail(frame, + EXT_IMAGE_COPY_CAPTURE_FRAME_V1_FAILURE_REASON_UNKNOWN); + } + + return ok; +} + static void frame_handle_destroy(struct wl_client *client, struct wl_resource *frame_resource) { wl_resource_destroy(frame_resource); @@ -403,6 +474,7 @@ static void session_destroy(struct wlr_ext_image_copy_capture_session_v1 *sessio wl_resource_set_user_data(session->resource, NULL); pixman_region32_fini(&session->damage); + wlr_drm_syncobj_timeline_unref(session->copy_timeline); wl_list_remove(&session->source_destroy.link); wl_list_remove(&session->source_constraints_update.link); wl_list_remove(&session->source_frame.link);