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 <robert.mader@collabora.com>
This commit is contained in:
Robert Mader 2025-09-03 17:57:19 +02:00
parent 07a5ec8157
commit 9ea205e007
7 changed files with 139 additions and 44 deletions

View file

@ -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);

View file

@ -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");
}

View file

@ -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;
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 (wl_resource_get_version(csrc->resource) >=
WESTON_CAPTURE_SOURCE_V1_FORMATS_DONE_SINCE_VERSION)
}
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;
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);
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);

View file

@ -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);

View file

@ -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;
}

View file

@ -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. */

View file

@ -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);