From 0dfddd703a661486ba37c3ba27990ef1706261e1 Mon Sep 17 00:00:00 2001 From: Greg Lamberson Date: Sat, 9 May 2026 14:58:12 -0500 Subject: [PATCH] ext-image-copy-capture: emit capture_time event Sample CLOCK_MONOTONIC at the start of copy_dmabuf and copy_shm, store the value on the frame, and emit it before the ready event. The timestamp marks the moment the compositor latched source content for this capture, giving capture clients a stable anchor from which to compute the budget remaining in the current frame interval and to use as the source timestamp on any downstream encoded stream. No render timer, no timeline waiter, no dependency on the explicit sync MR (wayland-protocols !506). The capture path returns to its previous shape with one additional clock_gettime call per frame. IMAGE_COPY_CAPTURE_MANAGER_V1_VERSION bumped to 2. Signed-off-by: Greg Lamberson --- .../wlr/types/wlr_ext_image_copy_capture_v1.h | 5 ++- types/wlr_ext_image_copy_capture_v1.c | 31 ++++++++++++++----- 2 files changed, 27 insertions(+), 9 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..ecc10978a 100644 --- a/include/wlr/types/wlr_ext_image_copy_capture_v1.h +++ b/include/wlr/types/wlr_ext_image_copy_capture_v1.h @@ -10,9 +10,10 @@ #define WLR_TYPES_WLR_EXT_IMAGE_COPY_CAPTURE_V1_H #include +#include +#include #include #include -#include struct wlr_renderer; @@ -58,6 +59,8 @@ struct wlr_ext_image_copy_capture_frame_v1 { struct { struct wlr_ext_image_copy_capture_session_v1 *session; + struct timespec capture_time; + bool capture_time_valid; } WLR_PRIVATE; }; diff --git a/types/wlr_ext_image_copy_capture_v1.c b/types/wlr_ext_image_copy_capture_v1.c index 2e969b4a9..9807bce16 100644 --- a/types/wlr_ext_image_copy_capture_v1.c +++ b/types/wlr_ext_image_copy_capture_v1.c @@ -9,7 +9,7 @@ #include "ext-image-copy-capture-v1-protocol.h" #include "render/pixel_format.h" -#define IMAGE_COPY_CAPTURE_MANAGER_V1_VERSION 1 +#define IMAGE_COPY_CAPTURE_MANAGER_V1_VERSION 2 struct wlr_ext_image_copy_capture_cursor_session_v1 { struct wl_resource *resource; @@ -101,13 +101,25 @@ void wlr_ext_image_copy_capture_frame_v1_ready(struct wlr_ext_image_copy_capture ext_image_copy_capture_frame_v1_send_transform(frame->resource, transform); ext_image_copy_capture_frame_v1_send_presentation_time(frame->resource, pres_time_sec >> 32, (uint32_t)pres_time_sec, presentation_time->tv_nsec); + + if (frame->capture_time_valid && + wl_resource_get_version(frame->resource) >= + EXT_IMAGE_COPY_CAPTURE_FRAME_V1_CAPTURE_TIME_SINCE_VERSION) { + uint64_t cap_sec = (uint64_t)frame->capture_time.tv_sec; + ext_image_copy_capture_frame_v1_send_capture_time(frame->resource, + cap_sec >> 32, (uint32_t)cap_sec, frame->capture_time.tv_nsec); + } + ext_image_copy_capture_frame_v1_send_ready(frame->resource); 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_ext_image_copy_capture_frame_v1 *frame, + struct wlr_buffer *dst, struct wlr_buffer *src, + struct wlr_renderer *renderer, const pixman_region32_t *clip) { + clock_gettime(CLOCK_MONOTONIC, &frame->capture_time); + frame->capture_time_valid = true; + struct wlr_texture *texture = wlr_texture_from_buffer(renderer, src); if (texture == NULL) { return false; @@ -132,8 +144,12 @@ out: return ok; } -static bool copy_shm(void *data, uint32_t format, size_t stride, +static bool copy_shm(struct wlr_ext_image_copy_capture_frame_v1 *frame, + void *data, uint32_t format, size_t stride, struct wlr_buffer *src, struct wlr_renderer *renderer) { + clock_gettime(CLOCK_MONOTONIC, &frame->capture_time); + frame->capture_time_valid = true; + // TODO: bypass renderer if source buffer supports data ptr access struct wlr_texture *texture = wlr_texture_from_buffer(renderer, src); if (!texture) { @@ -148,7 +164,6 @@ static bool copy_shm(void *data, uint32_t format, size_t stride, }); wlr_texture_destroy(texture); - return ok; } @@ -174,7 +189,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(frame, dst, src, renderer, &frame->buffer_damage); } } else if (wlr_buffer_begin_data_ptr_access(dst, WLR_BUFFER_DATA_PTR_ACCESS_WRITE, &data, &format, &stride)) { @@ -182,7 +197,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(frame, data, format, stride, src, renderer); } wlr_buffer_end_data_ptr_access(dst); }