[Git][xorg-team/wayland/weston][upstream-experimental] 9 commits: vulkan-renderer: move capture commands before present

Dylan Aïssi (@daissi) <[email protected]> Thu, 09 Jul 2026 08:18:33 +0000
Newsgroups gmane.linux.debian.devel.x
Message-ID <[email protected]>

Dylan Aïssi pushed to branch upstream-experimental at X Strike Force / wayland / weston


Commits:
a10bff42 by Erico Nunes at 2026-07-01T09:46:58+02:00
vulkan-renderer: move capture commands before present

At least for the swapchain output case, it is not allowed by the
spec to do anything with the VkImage after it has been passed to
vkQueuePresentKHR.
Solve this by moving the capture commands to be recorded before
the presentation.

Signed-off-by: Erico Nunes <[email protected]>

- - - - -
054f103c by Erico Nunes at 2026-07-01T09:49:53+02:00
vulkan-renderer: initialize image handle for swapchain outputs

This is used in the read pixels path and causes a crash if not
initialized.

Signed-off-by: Erico Nunes <[email protected]>

- - - - -
a50dcb9b by Erico Nunes at 2026-07-01T09:49:53+02:00
vulkan-renderer: fix layout for swapchain output capture

The capture code should transition from a PRESENT_SRC layout
when the output is a swapchain.

Signed-off-by: Erico Nunes <[email protected]>

- - - - -
a8b6adc7 by Erico Nunes at 2026-07-01T09:49:53+02:00
vulkan-renderer: fix read pixels for output with borders

The stride and origin of the source buffer may not match the destination
buffer for capture. Notably this is the case when capturing a screenshot
of a buffer with borders (e.g. wayland backend with decoration).
Fix it by adjusting the calculated regions for the capture considering
each stride separately.

Signed-off-by: Erico Nunes <[email protected]>

- - - - -
4418efdb by Daniel Stone at 2026-07-01T15:37:45+01:00
vulkan-renderer: Properly sync texture upload against itself

SHM texture uploads follow the same model as GL, where the surface
itself owns a single VkBuffer (for staging) and VkImage (for sampling).
Every client SHM buffer gets uploaded to this staging buffer and image
during flush_damage().

The renderer was diligently inserting the right Vulkan pipeline barriers
to avoid any races between output repaint sampling the textures, and
damage flush uploading the textures.

Given how asynchronous Vulkan is, it was also using a VkFence to
protect against itself: placing a fence on the vkQueueSubmit which will
be signaled when the VkBuffer -> VkImage copy has finished (so the
staging buffer is free for reuse), and then waiting for that before
doing more uploads.

Unfortunately the wait was after we copied into the VkBuffer's memory
map.

Fixes flakes in output-damage test, which showed this up really nicely:
the first repaint (issued asynchronously with no wait as it was not
captured for a screenshot) proceeded, then the second repaint would
overwrite it with all cyan (for the second buffer which would get
screenshot), as the second texture upload could complete the memcpy into
the staging VkBuffer before the first VkBuffer -> VkImage copy had
completed.

This was visible by noting that the flake could be hidden by taking a
screenshot after the first grey-only surface commit - to force a full
pipeline stall immediately after repaint - or just a client-side sleep
immediately after it.

Signed-off-by: Daniel Stone <[email protected]>

- - - - -
0819d0a4 by Robert Mader at 2026-07-02T12:55:57+02:00
backend-drm: Tie plane color pipeline support to color-management

We currently enable DRM_CLIENT_CAP_PLANE_COLOR_PIPELINE unconditionally,
even color-management is not enabled. Unfortunately this disables the
legacy COLOR_ENCODING and COLOR_RANGE DRM properties in the kernel,
breaking YCbCr offloading in various cases. This is worsened by the fact
that the client cap is supported by recent kernels unconditionally, even
for drivers without any color pipeline support(1).

In the future there will be a color pipeline replacement for the legacy
properties(2), which we'll want to use whenever available. However that has
neither landed in the kernel nor do we support it Weston yet.

Only enable DRM_CLIENT_CAP_PLANE_COLOR_PIPELINE if color-management is
enabled for now.

1: https://lore.kernel.org/dri-devel/[email protected]/
2: https://lore.kernel.org/dri-devel/[email protected]/

Signed-off-by: Robert Mader <[email protected]>

- - - - -
5518f8a6 by Marius Vlad at 2026-07-03T12:48:04+02:00
gitlab-ci: Bump libwayland to 1.24 and libdrm to 2.4.130

This former is required to grab wl_display_dispatch_timeout() and the
later for DRM_CLIENT_CAP_PLANE_COLOR_PIPELINE.

Signed-off-by: Marius Vlad <[email protected]>

- - - - -
f970a16d by Marius Vlad at 2026-07-07T11:51:11+03:00
build: bump to version 15.0.93 for the RC1 release

Signed-off-by: Marius Vlad <[email protected]>

- - - - -
d853611c by Dylan Aïssi at 2026-07-09T10:10:13+02:00
New upstream version 15.0.93
- - - - -


5 changed files:

- .gitlab-ci.yml
- .gitlab-ci/build-deps.sh
- libweston/backend-drm/kms.c
- libweston/renderer-vulkan/vulkan-renderer.c
- meson.build


Changes:

=====================================
.gitlab-ci.yml
=====================================
@@ -43,7 +43,7 @@
 variables:
   FDO_UPSTREAM_REPO: wayland/weston
   FDO_REPO_SUFFIX: "$BUILD_OS-$FDO_DISTRIBUTION_VERSION/$BUILD_ARCH"
-  FDO_DISTRIBUTION_TAG: '2026-06-15-linux-7.1-mesa-26.1.2'
+  FDO_DISTRIBUTION_TAG: '2026-07-02-libwayland-1.24-libdrm-2.4.130'
 
 
 include:


=====================================
.gitlab-ci/build-deps.sh
=====================================
@@ -106,7 +106,7 @@ fdo_log_section_end install_kernel
 # Build and install Wayland; keep this version in sync with our dependency
 # in meson.build.
 fdo_log_section_start_collapsed install_wayland "install_wayland"
-git clone --branch 1.22.0 --depth=1 https://gitlab.freedesktop.org/wayland/wayland
+git clone --branch 1.24.0 --depth=1 https://gitlab.freedesktop.org/wayland/wayland
 cd wayland
 git show -s HEAD
 meson setup build --wrap-mode=nofallback -Ddocumentation=false
@@ -126,12 +126,10 @@ cd ..
 rm -rf wayland-protocols
 fdo_log_section_end install_wayland
 
-# Build and install our own version of libdrm. Debian 11 (bullseye) provides
-# libdrm 2.4.104 which doesn't have the IN_FORMATS iterator api, and Mesa
-# depends on 2.4.109 as well.
-# Bump to 2.4.118 to include DRM_FORMAT_NV{15,20,30}
+# Build and install our own version of libdrm.
+# 2.4.130 is the first version defining DRM_CLIENT_CAP_PLANE_COLOR_PIPELINE.
 fdo_log_section_start_collapsed install_libdrm "install_libdrm"
-git clone --branch libdrm-2.4.118 --depth=1 https://gitlab.freedesktop.org/mesa/drm.git
+git clone --branch libdrm-2.4.130 --depth=1 https://gitlab.freedesktop.org/mesa/drm.git
 cd drm
 meson setup build --wrap-mode=nofallback -Dauto_features=disabled \
 	-Dvc4=disabled -Dfreedreno=disabled -Detnaviv=disabled


=====================================
libweston/backend-drm/kms.c
=====================================
@@ -2629,8 +2629,20 @@ init_kms_caps(struct drm_device *device)
 	drmSetClientCap(device->kms_device->fd, DRM_CLIENT_CAP_WRITEBACK_CONNECTORS, 1);
 
 #ifdef DRM_CLIENT_CAP_PLANE_COLOR_PIPELINE
-	ret = drmSetClientCap(device->kms_device->fd, DRM_CLIENT_CAP_PLANE_COLOR_PIPELINE, 1);
-	device->color_pipeline_supported = (ret == 0);
+	/*
+	 * color_manager being present at this point implies color-management
+	 * being enabled as the no-op color manager will only be created after
+	 * all backends are loaded. Only enable plane color pipelines in that
+	 * case as there is no color pipeline replacement for the legacy
+	 * COLOR_ENCODING and COLOR_RANGE properties yet.
+	 */
+	if (b->compositor->color_manager) {
+		ret = drmSetClientCap(device->kms_device->fd,
+				      DRM_CLIENT_CAP_PLANE_COLOR_PIPELINE, 1);
+		device->color_pipeline_supported = (ret == 0);
+	} else {
+		device->color_pipeline_supported = false;
+	}
 #else
 	device->color_pipeline_supported = false;
 #endif


=====================================
libweston/renderer-vulkan/vulkan-renderer.c
=====================================
@@ -1305,14 +1305,17 @@ vulkan_renderer_cmd_end_wait(struct vulkan_renderer *vr,
 static bool
 vulkan_renderer_do_read_pixels(struct vulkan_renderer *vr,
 			       VkImage color_attachment,
+			       VkImageLayout image_layout,
 			       struct vulkan_output_state *vo,
 			       const struct pixel_format_info *pixel_format,
-			       void *pixels, int stride,
+			       void *pixels, int dst_stride,
+			       int dst_x, int dst_y,
 			       const struct weston_geometry *rect)
 {
 	VkBuffer dst_buffer;
 	VkDeviceMemory dst_memory;
-	VkDeviceSize buffer_size = stride * vo->fb_size.height;
+	int src_stride = vo->fb_size.width * (pixel_format->bpp / 8);
+	VkDeviceSize buffer_size = src_stride * vo->fb_size.height;
 	VkResult result;
 
 	create_buffer(vr, buffer_size,
@@ -1325,7 +1328,7 @@ vulkan_renderer_do_read_pixels(struct vulkan_renderer *vr,
 	vulkan_renderer_cmd_begin(vr, &cmd_buffer);
 
 	transition_image_layout(cmd_buffer, color_attachment,
-				VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
+				image_layout, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
 				VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,
 				VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT,
 				VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_IGNORED);
@@ -1333,13 +1336,13 @@ vulkan_renderer_do_read_pixels(struct vulkan_renderer *vr,
 	copy_sub_image_to_buffer(cmd_buffer,
 				 dst_buffer, color_attachment,
 				 vo->fb_size.width, vo->fb_size.height,
-				 (stride / (pixel_format->bpp/8)),
+				 vo->fb_size.width,
 				 pixel_format->bpp,
 				 rect->x, rect->y,
 				 rect->width, rect->height);
 
 	transition_image_layout(cmd_buffer, color_attachment,
-				VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
+				VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, image_layout,
 				VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
 				VK_ACCESS_TRANSFER_READ_BIT, VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
 				VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_IGNORED);
@@ -1348,7 +1351,7 @@ vulkan_renderer_do_read_pixels(struct vulkan_renderer *vr,
 	vulkan_renderer_cmd_end_wait(vr, &cmd_buffer);
 
 	/* Map image memory so we can start copying from it */
-	void* buffer_map;
+	void *buffer_map;
 	result = vkMapMemory(vr->dev, dst_memory, 0, VK_WHOLE_SIZE, 0, &buffer_map);
 	check_vk_success(result, "vkMapMemory");
 
@@ -1358,12 +1361,12 @@ vulkan_renderer_do_read_pixels(struct vulkan_renderer *vr,
 	pixman_image_t *image_src;
 	image_src = pixman_image_create_bits_no_clear(pixel_format->pixman_format,
 						      vo->fb_size.width, vo->fb_size.height,
-						      buffer_map, stride);
+						      buffer_map, src_stride);
 
 	pixman_image_t *image_dst;
 	image_dst = pixman_image_create_bits_no_clear(pixel_format->pixman_format,
-						      vo->fb_size.width, vo->fb_size.height,
-						      pixels, stride);
+						      rect->width, rect->height,
+						      pixels, dst_stride);
 
 	pixman_image_composite32(PIXMAN_OP_SRC,
 				 image_src,        /* src */
@@ -1371,7 +1374,7 @@ vulkan_renderer_do_read_pixels(struct vulkan_renderer *vr,
 				 image_dst,        /* dest */
 				 rect->x, rect->y, /* src x,y */
 				 0, 0,             /* mask x,y */
-				 rect->x, rect->y, /* dest x,y */
+				 dst_x, dst_y,     /* dest x,y */
 				 rect->width, rect->height);
 
 	pixman_image_unref(image_src);
@@ -1385,6 +1388,7 @@ vulkan_renderer_do_read_pixels(struct vulkan_renderer *vr,
 static bool
 vulkan_renderer_do_capture(struct vulkan_renderer *vr,
 			   VkImage color_attachment,
+			   VkImageLayout image_layout,
 			   struct vulkan_output_state *vo,
 			   struct weston_buffer *into,
 			   const struct weston_geometry *rect)
@@ -1398,8 +1402,10 @@ vulkan_renderer_do_capture(struct vulkan_renderer *vr,
 
 	wl_shm_buffer_begin_access(shm);
 
-	ret = vulkan_renderer_do_read_pixels(vr, color_attachment, vo, pixel_format,
-					     wl_shm_buffer_get_data(shm), into->stride, rect);
+	/* This always copies the requested rect into the start
+	 * of the target shm, so dest coords 0,0 */
+	ret = vulkan_renderer_do_read_pixels(vr, color_attachment, image_layout, vo, pixel_format,
+					     wl_shm_buffer_get_data(shm), into->stride, 0, 0, rect);
 
 	wl_shm_buffer_end_access(shm);
 
@@ -1409,6 +1415,7 @@ vulkan_renderer_do_capture(struct vulkan_renderer *vr,
 static void
 vulkan_renderer_do_capture_tasks(struct vulkan_renderer *vr,
 				 VkImage color_attachment,
+				 VkImageLayout image_layout,
 				 struct weston_output *output,
 				 enum weston_output_capture_source source)
 {
@@ -1454,7 +1461,8 @@ vulkan_renderer_do_capture_tasks(struct vulkan_renderer *vr,
 			continue;
 		}
 
-		if (vulkan_renderer_do_capture(vr, color_attachment, vo, buffer, &rect))
+		if (vulkan_renderer_do_capture(vr, color_attachment, image_layout,
+					       vo, buffer, &rect))
 			weston_capture_task_retire_complete(ct);
 		else
 			weston_capture_task_retire_failed(ct, "Vulkan: capture failed");
@@ -2346,6 +2354,7 @@ vulkan_renderer_create_swapchain(struct weston_output *output,
 
 	for (uint32_t i = 0; i < vo->image_count; i++) {
 		struct vulkan_renderer_image *im = &vo->images[i];
+		im->image = swapchain_images[i];
 
 		create_image_view(vr->dev, swapchain_images[i], format, &im->image_view);
 		create_framebuffer(vr->dev, vo->renderpass, im->image_view,
@@ -2386,6 +2395,7 @@ vulkan_renderer_repaint_output(struct weston_output *output,
 	struct weston_paint_node *pnode;
 	VkResult result;
 	uint32_t swapchain_index;
+	VkImageLayout expected_layout;
 
 	assert(vo);
 	assert(!renderbuffer ||
@@ -2442,11 +2452,13 @@ vulkan_renderer_repaint_output(struct weston_output *output,
 
 		im = &vo->images[swapchain_index];
 		rb = im->renderbuffer;
+		expected_layout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
 		break;
 	case VULKAN_OUTPUT_HEADLESS:
 		assert(renderbuffer);
 		rb = renderbuffer;
 		im = rb->image;
+		expected_layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
 		break;
 	default:
 		abort();
@@ -2557,6 +2569,34 @@ vulkan_renderer_repaint_output(struct weston_output *output,
 	result = vkQueueSubmit(vr->queue, 1, &submit_info, fr->fence);
 	check_vk_success(result, "vkQueueSubmit");
 
+	vulkan_renderer_do_capture_tasks(vr, im->image, expected_layout, output,
+					 WESTON_OUTPUT_CAPTURE_SOURCE_FRAMEBUFFER);
+	vulkan_renderer_do_capture_tasks(vr, im->image, expected_layout, output,
+					 WESTON_OUTPUT_CAPTURE_SOURCE_FULL_FRAMEBUFFER);
+
+	if (rb->buffer) {
+		uint32_t *pixels = rb->buffer;
+		int width = vo->fb_size.width;
+		int stride = width * (compositor->read_format->bpp >> 3);
+		pixman_box32_t extents;
+
+		assert(rb->stride == stride);
+
+		extents = weston_matrix_transform_rect(&output->matrix,
+						       rb->damage.extents);
+
+		const struct weston_geometry rect = {
+			.x = vo->area.x + extents.x1,
+			.y = vo->area.y + extents.y1,
+			.width = extents.x2 - extents.x1,
+			.height = extents.y2 - extents.y1,
+		};
+
+		vulkan_renderer_do_read_pixels(vr, im->image, expected_layout,
+					       vo, compositor->read_format,
+					       pixels, stride, rect.x, rect.y, &rect);
+	}
+
 	if (vo->output_type == VULKAN_OUTPUT_SWAPCHAIN) {
 		assert(vulkan_device_has(vr, EXTENSION_KHR_SWAPCHAIN));
 
@@ -2613,39 +2653,11 @@ vulkan_renderer_repaint_output(struct weston_output *output,
 		fd_update(&vo->render_fence_fd, fd);
 	}
 
-	vulkan_renderer_do_capture_tasks(vr, im->image, output,
-					 WESTON_OUTPUT_CAPTURE_SOURCE_FRAMEBUFFER);
-	vulkan_renderer_do_capture_tasks(vr, im->image, output,
-					 WESTON_OUTPUT_CAPTURE_SOURCE_FULL_FRAMEBUFFER);
-
 	rb->border_status = BORDER_STATUS_CLEAN;
 	vo->border_status = BORDER_STATUS_CLEAN;
 
 	update_buffer_release_fences(compositor, output);
 
-	if (rb->buffer) {
-		uint32_t *pixels = rb->buffer;
-		int width = vo->fb_size.width;
-		int stride = width * (compositor->read_format->bpp >> 3);
-		pixman_box32_t extents;
-
-		assert(rb->stride == stride);
-
-		extents = weston_matrix_transform_rect(&output->matrix,
-						       rb->damage.extents);
-
-		const struct weston_geometry rect = {
-			.x = vo->area.x + extents.x1,
-			.y = vo->area.y + extents.y1,
-			.width = extents.x2 - extents.x1,
-			.height = extents.y2 - extents.y1,
-		};
-
-		vulkan_renderer_do_read_pixels(vr, im->image, vo,
-					       compositor->read_format,
-					       pixels, stride, &rect);
-	}
-
 	pixman_region32_clear(&rb->damage);
 
 	vo->frame_index = (vo->frame_index + 1) % vo->num_frames;
@@ -2714,13 +2726,13 @@ update_texture_image(struct vulkan_renderer *vr,
 	VkDeviceSize image_size = pitch * buffer_height * (pixel_format->bpp/8);
 	VkResult result;
 
+	vkWaitForFences(vr->dev, 1, &texture->upload_fence, VK_TRUE, UINT64_MAX);
+	vkResetFences(vr->dev, 1, &texture->upload_fence);
+
 	assert(pixels);
 
 	memcpy(texture->staging_map, pixels, (size_t)image_size);
 
-	vkWaitForFences(vr->dev, 1, &texture->upload_fence, VK_TRUE, UINT64_MAX);
-	vkResetFences(vr->dev, 1, &texture->upload_fence);
-
 	const VkCommandBufferBeginInfo begin_info = {
 		.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
 		.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,


=====================================
meson.build
=====================================
@@ -1,6 +1,6 @@
 project('weston',
 	['c', 'cpp'],
-	version: '15.0.92',
+	version: '15.0.93',
 	default_options: [
 		'warning_level=3',
 		'c_std=gnu11',



View it on GitLab: https://salsa.debian.org/xorg-team/wayland/weston/-/compare/fb163cf52d40f337acec41625547cf32bd2943dc...d853611cb8dd3f140c5243ba67590ee5881fb998

-- 
View it on GitLab: https://salsa.debian.org/xorg-team/wayland/weston/-/compare/fb163cf52d40f337acec41625547cf32bd2943dc...d853611cb8dd3f140c5243ba67590ee5881fb998
You're receiving this email because of your account on salsa.debian.org. Manage all notifications: https://salsa.debian.org/-/profile/notifications | Help: https://salsa.debian.org/help