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..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,9 +12,11 @@ #include #include #include +#include #include struct wlr_renderer; +struct wlr_drm_syncobj_timeline; struct wlr_ext_image_copy_capture_manager_v1 { struct wl_global *global; @@ -43,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; }; @@ -58,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; }; @@ -71,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. * @@ -80,8 +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_buffer *src, struct wlr_renderer *renderer, + 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 0e3a57823..2664e74de 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, @@ -85,10 +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)) { - wlr_ext_image_copy_capture_frame_v1_ready(frame, - source->output->transform, &event->when); + event->buffer, source->output->renderer, + 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); + } } } @@ -152,6 +163,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 +294,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) { @@ -286,16 +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)) { + src_buffer, cursor_source->output->renderer, + 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 7d4b8928a..5ee3e9a58 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; @@ -148,10 +153,23 @@ 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)) { - wlr_ext_image_copy_capture_frame_v1_ready(frame, - source->output.transform, &event->when); + event->buffer, source->output.renderer, + 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); + } } } @@ -162,7 +180,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 +205,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 +269,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 +347,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..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); } @@ -105,16 +108,21 @@ 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_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; } @@ -123,6 +131,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 +143,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 +156,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,8 +166,16 @@ 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_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, @@ -162,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; @@ -174,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); + 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)) { @@ -182,7 +228,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); } @@ -200,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); @@ -397,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);