From 9ea205e0072287f694077e0fe6aefca1f08c8b2b Mon Sep 17 00:00:00 2001 From: Robert Mader Date: Wed, 3 Sep 2025 17:57:19 +0200 Subject: [PATCH] output-capture: Support writeback connector formats Writeback connectors often support multiple formats, fully independent of the input framebuffer. Wire up support for querying formats and send them to clients. Signed-off-by: Robert Mader --- libweston/backend-drm/drm.c | 23 ++--- libweston/backend-pipewire/pipewire.c | 3 +- libweston/output-capture.c | 105 +++++++++++++++++--- libweston/output-capture.h | 6 +- libweston/pixman-renderer.c | 16 ++- libweston/renderer-gl/gl-renderer.c | 9 +- libweston/renderer-vulkan/vulkan-renderer.c | 21 ++-- 7 files changed, 139 insertions(+), 44 deletions(-) diff --git a/libweston/backend-drm/drm.c b/libweston/backend-drm/drm.c index a3deaa6ee..431a91a58 100644 --- a/libweston/backend-drm/drm.c +++ b/libweston/backend-drm/drm.c @@ -634,11 +634,6 @@ drm_output_find_compatible_writeback(struct drm_output *output) if (!(possible_crtcs & (1 << output->crtc->pipe))) continue; - /* Does the writeback connector support the output gbm format? */ - if (!weston_drm_format_array_find_format(&wb->formats, - output->format->format)) - continue; - return wb; } @@ -691,13 +686,14 @@ drm_output_pick_writeback_capture_task(struct drm_output *output) const char *msg; int32_t width = output->base.current_mode->width; int32_t height = output->base.current_mode->height; - uint32_t format = output->format->format; + const struct weston_drm_format_array *writeback_formats = + weston_output_get_writeback_formats(&output->base); assert(output->device->atomic_modeset); ct = weston_output_pull_capture_task(&output->base, WESTON_OUTPUT_CAPTURE_SOURCE_WRITEBACK, - width, height, pixel_format_get_info(format)); + width, height, NULL, writeback_formats); if (!ct) return; @@ -716,7 +712,6 @@ drm_output_pick_writeback_capture_task(struct drm_output *output) buffer = weston_capture_task_get_buffer(ct); assert(buffer->width == width); assert(buffer->height == height); - assert(buffer->pixel_format->format == output->format->format); output->wb_state = drm_writeback_state_alloc(); if (!output->wb_state) { @@ -724,7 +719,8 @@ drm_output_pick_writeback_capture_task(struct drm_output *output) goto err; } - output->wb_state->fb = drm_fb_create_dumb(output->device, width, height, format); + output->wb_state->fb = drm_fb_create_dumb(output->device, width, height, + buffer->pixel_format->format); if (!output->wb_state->fb) { msg = "drm: failed to create dumb buffer for writeback state"; goto err_fb; @@ -1234,7 +1230,8 @@ drm_output_apply_mode(struct drm_output *output) WESTON_OUTPUT_CAPTURE_SOURCE_WRITEBACK, output->base.current_mode->width, output->base.current_mode->height, - pixel_format_get_info(output->format->format)); + NULL, + weston_output_get_writeback_formats(&output->base)); return 0; } @@ -2728,10 +2725,12 @@ drm_output_enable(struct weston_output *base) output->base.switch_mode = drm_output_switch_mode; if (device->atomic_modeset) - weston_output_update_capture_info(base, WESTON_OUTPUT_CAPTURE_SOURCE_WRITEBACK, + weston_output_update_capture_info(base, + WESTON_OUTPUT_CAPTURE_SOURCE_WRITEBACK, base->current_mode->width, base->current_mode->height, - pixel_format_get_info(output->format->format)); + NULL, + weston_output_get_writeback_formats(&output->base)); weston_log("Output %s (crtc %d) video modes:\n", output->base.name, output->crtc->crtc_id); diff --git a/libweston/backend-pipewire/pipewire.c b/libweston/backend-pipewire/pipewire.c index 4236adaf6..9921f958b 100644 --- a/libweston/backend-pipewire/pipewire.c +++ b/libweston/backend-pipewire/pipewire.c @@ -1092,7 +1092,8 @@ out: while ((ct = weston_output_pull_capture_task(base, WESTON_OUTPUT_CAPTURE_SOURCE_FRAMEBUFFER, width, height, - ec->read_format))) + ec->read_format, + NULL))) weston_capture_task_retire_failed(ct, "No pipewire buffer"); } diff --git a/libweston/output-capture.c b/libweston/output-capture.c index 7240ca722..616e86d94 100644 --- a/libweston/output-capture.c +++ b/libweston/output-capture.c @@ -120,7 +120,12 @@ struct weston_output_capture_source_info { int width; int height; + + /* Format used for non-writeback capture source */ uint32_t drm_format; + + /* Formats supported for writeback capture source */ + struct weston_drm_format_array writeback_formats; }; /** Capture records for an output */ @@ -150,8 +155,10 @@ weston_output_capture_info_create(void) * Initialize to no sources available by leaving * width, height and drm_format as zero. */ - for (i = 0; i < ARRAY_LENGTH(ci->source_info); i++) + for (i = 0; i < ARRAY_LENGTH(ci->source_info); i++) { ci->source_info[i].pixel_source = i; + weston_drm_format_array_init(&ci->source_info[i].writeback_formats); + } return ci; } @@ -162,6 +169,7 @@ weston_output_capture_info_destroy(struct weston_output_capture_info **cip) { struct weston_output_capture_info *ci = *cip; struct weston_capture_source *csrc, *tmp; + unsigned i; assert(ci); @@ -178,6 +186,9 @@ weston_output_capture_info_destroy(struct weston_output_capture_info **cip) assert(wl_list_empty(&ci->pending_capture_list)); + for (i = 0; i < ARRAY_LENGTH(ci->source_info); i++) + weston_drm_format_array_fini(&ci->source_info[i].writeback_formats); + free(ci); *cip = NULL; } @@ -198,8 +209,14 @@ weston_output_capture_info_repaint_done(struct weston_output_capture_info *ci) static bool source_info_is_available(const struct weston_output_capture_source_info *csi) { - return csi->width > 0 && csi->height > 0 && - csi->drm_format != DRM_FORMAT_INVALID; + if (csi->pixel_source == WESTON_OUTPUT_CAPTURE_SOURCE_WRITEBACK && + weston_drm_format_array_count_pairs(&csi->writeback_formats) == 0) + return false; + else if (csi->pixel_source != WESTON_OUTPUT_CAPTURE_SOURCE_WRITEBACK && + csi->drm_format == DRM_FORMAT_INVALID) + return false; + + return csi->width > 0 && csi->height > 0; } static void @@ -209,13 +226,28 @@ capture_info_send_source_info(struct weston_output_capture_info *ci, struct weston_capture_source *csrc; wl_list_for_each(csrc, &ci->capture_source_list, link) { + bool send_done; + if (csrc->pixel_source != csi->pixel_source) continue; - weston_capture_source_v1_send_format(csrc->resource, - csi->drm_format); - if (wl_resource_get_version(csrc->resource) >= - WESTON_CAPTURE_SOURCE_V1_FORMATS_DONE_SINCE_VERSION) + send_done = wl_resource_get_version(csrc->resource) >= + WESTON_CAPTURE_SOURCE_V1_FORMATS_DONE_SINCE_VERSION; + + if (csi->pixel_source == WESTON_OUTPUT_CAPTURE_SOURCE_WRITEBACK) { + struct weston_drm_format *fmt; + + wl_array_for_each(fmt, &csi->writeback_formats.arr) { + weston_capture_source_v1_send_format(csrc->resource, + fmt->format); + if (!send_done) + break; + } + } else { + weston_capture_source_v1_send_format(csrc->resource, + csi->drm_format); + } + if (send_done) weston_capture_source_v1_send_formats_done(csrc->resource); weston_capture_source_v1_send_size(csrc->resource, @@ -246,7 +278,9 @@ capture_info_get_csi(struct weston_output_capture_info *ci, * \param src The source type on the output. * \param width The new buffer width. * \param height The new buffer height. - * \param format The new pixel format. + * \param format The new pixel format or %NULL for writeback sources. + * \param writeback_formats An array of the supported pixel formats or %NULL for + * non-writeback sources. * * If any one of width, height or format is zero/NULL, the source becomes * unavailable to clients. Otherwise the source becomes available. @@ -257,21 +291,47 @@ WL_EXPORT void weston_output_update_capture_info(struct weston_output *output, enum weston_output_capture_source src, int width, int height, - const struct pixel_format_info *format) + const struct pixel_format_info *format, + const struct weston_drm_format_array *writeback_formats) { struct weston_output_capture_info *ci = output->capture_info; struct weston_output_capture_source_info *csi; + bool formats_equal; csi = capture_info_get_csi(ci, src); + if (src == WESTON_OUTPUT_CAPTURE_SOURCE_WRITEBACK) { + assert(!format); + if (writeback_formats) { + formats_equal = + weston_drm_format_array_equal(&csi->writeback_formats, + writeback_formats); + } else { + formats_equal = csi->writeback_formats.arr.size == 0; + } + } else { + assert(format && !writeback_formats); + formats_equal = (format->format == csi->drm_format); + } + if (csi->width == width && csi->height == height && - csi->drm_format == format->format) + formats_equal) return; csi->width = width; csi->height = height; - csi->drm_format = format->format; + if (src == WESTON_OUTPUT_CAPTURE_SOURCE_WRITEBACK) { + if (writeback_formats) { + weston_drm_format_array_replace(&csi->writeback_formats, + writeback_formats); + } else { + weston_drm_format_array_fini(&csi->writeback_formats); + weston_drm_format_array_init(&csi->writeback_formats); + } + } else { + csi->drm_format = format->format; + } if (source_info_is_available(csi)) { capture_info_send_source_info(ci, csi); @@ -305,9 +365,19 @@ static bool buffer_is_compatible(struct weston_buffer *buffer, struct weston_output_capture_source_info *csi) { + bool format_supported = false; + + if (csi->pixel_source == WESTON_OUTPUT_CAPTURE_SOURCE_WRITEBACK) { + format_supported = + weston_drm_format_array_find_format(&csi->writeback_formats, + buffer->pixel_format->format) != NULL; + } else { + format_supported = (buffer->pixel_format->format == csi->drm_format); + } + return buffer->width == csi->width && buffer->height == csi->height && - buffer->pixel_format->format == csi->drm_format && + format_supported && buffer->format_modifier == DRM_FORMAT_MOD_LINEAR; } @@ -401,7 +471,8 @@ WL_EXPORT struct weston_capture_task * weston_output_pull_capture_task(struct weston_output *output, enum weston_output_capture_source src, int width, int height, - const struct pixel_format_info *format) + const struct pixel_format_info *format, + const struct weston_drm_format_array *writeback_formats) { struct weston_output_capture_info *ci = output->capture_info; struct weston_output_capture_source_info *csi; @@ -416,7 +487,13 @@ weston_output_pull_capture_task(struct weston_output *output, csi = capture_info_get_csi(ci, src); assert(csi->width == width); assert(csi->height == height); - assert(csi->drm_format == format->format); + + if (src == WESTON_OUTPUT_CAPTURE_SOURCE_WRITEBACK) { + assert(weston_drm_format_array_equal(&csi->writeback_formats, + writeback_formats)); + } else { + assert(csi->drm_format == format->format); + } wl_list_for_each_safe(ct, tmp, &ci->pending_capture_list, link) { assert(ct->owner->output == output); diff --git a/libweston/output-capture.h b/libweston/output-capture.h index b710bf394..9ba82fd58 100644 --- a/libweston/output-capture.h +++ b/libweston/output-capture.h @@ -66,7 +66,8 @@ void weston_output_update_capture_info(struct weston_output *output, enum weston_output_capture_source src, int width, int height, - const struct pixel_format_info *format); + const struct pixel_format_info *format, + const struct weston_drm_format_array *writeback_formats); bool weston_output_has_renderer_capture_tasks(struct weston_output *output); @@ -77,7 +78,8 @@ struct weston_capture_task * weston_output_pull_capture_task(struct weston_output *output, enum weston_output_capture_source src, int width, int height, - const struct pixel_format_info *format); + const struct pixel_format_info *format, + const struct weston_drm_format_array *writeback_formats); struct weston_buffer * weston_capture_task_get_buffer(struct weston_capture_task *ct); diff --git a/libweston/pixman-renderer.c b/libweston/pixman-renderer.c index 84031b703..33e9b3837 100644 --- a/libweston/pixman-renderer.c +++ b/libweston/pixman-renderer.c @@ -609,9 +609,11 @@ pixman_renderer_do_capture_tasks(struct weston_output *output, int height = pixman_image_get_height(from); struct weston_capture_task *ct; + assert(source != WESTON_OUTPUT_CAPTURE_SOURCE_WRITEBACK); + while ((ct = weston_output_pull_capture_task(output, source, width, height, - pfmt))) { + pfmt, NULL))) { struct weston_buffer *buffer = weston_capture_task_get_buffer(ct); assert(buffer->width == width); @@ -1036,7 +1038,8 @@ pixman_renderer_resize_output(struct weston_output *output, WESTON_OUTPUT_CAPTURE_SOURCE_FRAMEBUFFER, po->fb_size.width, po->fb_size.height, - po->hw_format); + po->hw_format, + NULL); } /* Discard renderbuffers as a last step in order to emit discarded @@ -1059,7 +1062,8 @@ pixman_renderer_resize_output(struct weston_output *output, WESTON_OUTPUT_CAPTURE_SOURCE_BLENDING, po->fb_size.width, po->fb_size.height, - po->shadow_format); + po->shadow_format, + NULL); return !!po->shadow_image; } @@ -1177,7 +1181,8 @@ pixman_renderer_output_set_buffer(struct weston_output *output, WESTON_OUTPUT_CAPTURE_SOURCE_FRAMEBUFFER, po->fb_size.width, po->fb_size.height, - po->hw_format); + po->hw_format, + NULL); } static int @@ -1214,7 +1219,8 @@ pixman_renderer_output_create(struct weston_output *output, weston_output_update_capture_info(output, WESTON_OUTPUT_CAPTURE_SOURCE_FRAMEBUFFER, area.width, area.height, - options->format); + options->format, + NULL); return 0; } diff --git a/libweston/renderer-gl/gl-renderer.c b/libweston/renderer-gl/gl-renderer.c index 99126fdf4..f4a7b7cff 100644 --- a/libweston/renderer-gl/gl-renderer.c +++ b/libweston/renderer-gl/gl-renderer.c @@ -1274,7 +1274,8 @@ gl_renderer_do_capture_tasks(struct gl_renderer *gr, } while ((ct = weston_output_pull_capture_task(output, source, rect.width, - rect.height, format))) { + rect.height, format, + NULL))) { struct weston_buffer *buffer = weston_capture_task_get_buffer(ct); assert(buffer->width == rect.width); @@ -4193,12 +4194,14 @@ gl_renderer_resize_output(struct weston_output *output, weston_output_update_capture_info(output, WESTON_OUTPUT_CAPTURE_SOURCE_FRAMEBUFFER, area->width, area->height, - output->compositor->read_format); + output->compositor->read_format, + NULL); weston_output_update_capture_info(output, WESTON_OUTPUT_CAPTURE_SOURCE_FULL_FRAMEBUFFER, fb_size->width, fb_size->height, - output->compositor->read_format); + output->compositor->read_format, + NULL); /* Discard renderbuffers as a last step in order to emit discarded * callbacks once the renderer has correctly been updated. */ diff --git a/libweston/renderer-vulkan/vulkan-renderer.c b/libweston/renderer-vulkan/vulkan-renderer.c index 5573a69d7..23ba00fad 100644 --- a/libweston/renderer-vulkan/vulkan-renderer.c +++ b/libweston/renderer-vulkan/vulkan-renderer.c @@ -1450,7 +1450,8 @@ vulkan_renderer_do_capture_tasks(struct vulkan_renderer *vr, } while ((ct = weston_output_pull_capture_task(output, source, rect.width, - rect.height, pixel_format))) { + rect.height, pixel_format, + NULL))) { struct weston_buffer *buffer = weston_capture_task_get_buffer(ct); assert(buffer->width == rect.width); @@ -3270,12 +3271,14 @@ vulkan_renderer_resize_output(struct weston_output *output, weston_output_update_capture_info(output, WESTON_OUTPUT_CAPTURE_SOURCE_FRAMEBUFFER, area->width, area->height, - output->compositor->read_format); + output->compositor->read_format, + NULL); weston_output_update_capture_info(output, WESTON_OUTPUT_CAPTURE_SOURCE_FULL_FRAMEBUFFER, fb_size->width, fb_size->height, - output->compositor->read_format); + output->compositor->read_format, + NULL); if (!vulkan_renderer_discard_renderbuffers(vo, false)) return false; @@ -3630,12 +3633,14 @@ vulkan_renderer_output_surface_create(struct weston_output *output, weston_output_update_capture_info(output, WESTON_OUTPUT_CAPTURE_SOURCE_FRAMEBUFFER, area->width, area->height, - output->compositor->read_format); + output->compositor->read_format, + NULL); weston_output_update_capture_info(output, WESTON_OUTPUT_CAPTURE_SOURCE_FULL_FRAMEBUFFER, fb_size->width, fb_size->height, - output->compositor->read_format); + output->compositor->read_format, + NULL); vulkan_renderer_create_output_frames(output, fb_size, area, MAX_CONCURRENT_FRAMES); @@ -3670,12 +3675,14 @@ vulkan_renderer_output_surfaceless_create(struct weston_output *output, weston_output_update_capture_info(output, WESTON_OUTPUT_CAPTURE_SOURCE_FRAMEBUFFER, area->width, area->height, - output->compositor->read_format); + output->compositor->read_format, + NULL); weston_output_update_capture_info(output, WESTON_OUTPUT_CAPTURE_SOURCE_FULL_FRAMEBUFFER, fb_size->width, fb_size->height, - output->compositor->read_format); + output->compositor->read_format, + NULL); vulkan_renderer_create_output_frames(output, fb_size, area, MAX_CONCURRENT_FRAMES);