From ca54443b2c7068f621d70569d46385f1955bcbc4 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Wed, 6 May 2026 15:06:02 +0100 Subject: [PATCH 1/6] vulkan-renderer: Fix stages for sync in read_pixels When we're reading back from a framebuffer, we need to sync from fragment-shader output (i.e. after the final pixels are written to the framebuffer) before we copy away from it, and sync to fragment-shader (i.e. before any output touches the framebuffer) before we reuse it after the copy. The latter is a no-op as we currently do a complete vkQueueWaitIdle() after read_pixels(), but it removes a future footgun. Fixes occasional fixes seen in screenshot testing. Signed-off-by: Daniel Stone --- libweston/renderer-vulkan/vulkan-renderer.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/libweston/renderer-vulkan/vulkan-renderer.c b/libweston/renderer-vulkan/vulkan-renderer.c index 5d95858b8..9e01533d7 100644 --- a/libweston/renderer-vulkan/vulkan-renderer.c +++ b/libweston/renderer-vulkan/vulkan-renderer.c @@ -1349,8 +1349,8 @@ vulkan_renderer_do_read_pixels(struct vulkan_renderer *vr, transition_image_layout(cmd_buffer, color_attachment, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, - VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, - 0, VK_ACCESS_TRANSFER_WRITE_BIT); + VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT); copy_sub_image_to_buffer(cmd_buffer, dst_buffer, color_attachment, @@ -1362,8 +1362,8 @@ vulkan_renderer_do_read_pixels(struct vulkan_renderer *vr, transition_image_layout(cmd_buffer, color_attachment, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, - VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, - 0, VK_ACCESS_TRANSFER_WRITE_BIT); + 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); // TODO: async implementation of this, remove wait vulkan_renderer_cmd_end_wait(vr, &cmd_buffer); From c57f7bc2e56febeefedc5bcdf533aeebc4464dee Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Wed, 6 May 2026 15:50:47 +0100 Subject: [PATCH 2/6] vulkan-renderer: Fix stages for sync in create_swapchain Fix the stages for image-layout transitions to initialise UNDEFINED images we're going to use for our swapchain. Signed-off-by: Daniel Stone --- libweston/renderer-vulkan/vulkan-renderer.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libweston/renderer-vulkan/vulkan-renderer.c b/libweston/renderer-vulkan/vulkan-renderer.c index 9e01533d7..be20e3348 100644 --- a/libweston/renderer-vulkan/vulkan-renderer.c +++ b/libweston/renderer-vulkan/vulkan-renderer.c @@ -2374,8 +2374,8 @@ vulkan_renderer_create_swapchain(struct weston_output *output, transition_image_layout(cmd_buffer, swapchain_images[i], VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, - VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, - 0, VK_ACCESS_TRANSFER_WRITE_BIT); + VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, + 0, VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT); create_image_semaphores(vr, vo, im); From 89bd7bd5957472738eec30e6cbf103e27f5227d2 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Wed, 6 May 2026 16:00:11 +0100 Subject: [PATCH 3/6] vulkan-renderer: Fix stages for sync in texture upload When we're uploading data to a texture, the hazard we're protecting against before upload is ensuring reads from the fragment shader are complete before writes from the transfer queue begin. The synchronisation after the buffer -> image copy was correct. Signed-off-by: Daniel Stone --- libweston/renderer-vulkan/vulkan-renderer.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libweston/renderer-vulkan/vulkan-renderer.c b/libweston/renderer-vulkan/vulkan-renderer.c index be20e3348..4c4f882e8 100644 --- a/libweston/renderer-vulkan/vulkan-renderer.c +++ b/libweston/renderer-vulkan/vulkan-renderer.c @@ -2749,8 +2749,8 @@ update_texture_image(struct vulkan_renderer *vr, transition_image_layout(cmd_buffer, texture->image, expected_layout, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, - VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT); + VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_ACCESS_SHADER_READ_BIT, VK_ACCESS_TRANSFER_WRITE_BIT); copy_buffer_to_sub_image(cmd_buffer, texture->staging_buffer, texture->image, buffer_width, buffer_height, pitch, pixel_format->bpp, From 8253900353563dde4c09c2058575af7486802475 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Wed, 6 May 2026 16:02:40 +0100 Subject: [PATCH 4/6] vulkan-renderer: Take queue indices in layout transitions The only differences between this helper and transfer_image_queue_family() is that this took all the flags apart from queue indices, and QFOT took none of the flags apart from queue indices. To make the QFOT case correct, we have to pass almost all the stages and flags in, so we might as well just use a single helper. Add queue indices to this helper, which is a no-op for now. Signed-off-by: Daniel Stone --- libweston/renderer-vulkan/vulkan-renderer.c | 25 +++++++++++++-------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/libweston/renderer-vulkan/vulkan-renderer.c b/libweston/renderer-vulkan/vulkan-renderer.c index 4c4f882e8..76523b985 100644 --- a/libweston/renderer-vulkan/vulkan-renderer.c +++ b/libweston/renderer-vulkan/vulkan-renderer.c @@ -433,7 +433,8 @@ static void transition_image_layout(VkCommandBuffer cmd_buffer, VkImage image, VkImageLayout old_layout, VkImageLayout new_layout, VkPipelineStageFlags srcs, VkPipelineStageFlags dsts, - VkAccessFlags src_access, VkAccessFlags dst_access) + VkAccessFlags src_access, VkAccessFlags dst_access, + uint32_t src_index, uint32_t dst_index) { const VkImageMemoryBarrier barrier = { .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, @@ -445,8 +446,8 @@ transition_image_layout(VkCommandBuffer cmd_buffer, VkImage image, .subresourceRange.levelCount = 1, .srcAccessMask = src_access, .dstAccessMask = dst_access, - .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .srcQueueFamilyIndex = src_index, + .dstQueueFamilyIndex = dst_index, }; vkCmdPipelineBarrier(cmd_buffer, srcs, dsts, 0, 0, NULL, 0, NULL, 1, &barrier); @@ -1350,7 +1351,8 @@ vulkan_renderer_do_read_pixels(struct vulkan_renderer *vr, transition_image_layout(cmd_buffer, color_attachment, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, 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_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT, + VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_IGNORED); copy_sub_image_to_buffer(cmd_buffer, dst_buffer, color_attachment, @@ -1363,7 +1365,8 @@ vulkan_renderer_do_read_pixels(struct vulkan_renderer *vr, transition_image_layout(cmd_buffer, color_attachment, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, 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_ACCESS_TRANSFER_READ_BIT, VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, + VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_IGNORED); // TODO: async implementation of this, remove wait vulkan_renderer_cmd_end_wait(vr, &cmd_buffer); @@ -2375,7 +2378,8 @@ vulkan_renderer_create_swapchain(struct weston_output *output, transition_image_layout(cmd_buffer, swapchain_images[i], VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, - 0, VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT); + 0, VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, + VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_IGNORED); create_image_semaphores(vr, vo, im); @@ -2750,7 +2754,8 @@ update_texture_image(struct vulkan_renderer *vr, transition_image_layout(cmd_buffer, texture->image, expected_layout, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, - VK_ACCESS_SHADER_READ_BIT, VK_ACCESS_TRANSFER_WRITE_BIT); + VK_ACCESS_SHADER_READ_BIT, VK_ACCESS_TRANSFER_WRITE_BIT, + VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_IGNORED); copy_buffer_to_sub_image(cmd_buffer, texture->staging_buffer, texture->image, buffer_width, buffer_height, pitch, pixel_format->bpp, @@ -2759,7 +2764,8 @@ update_texture_image(struct vulkan_renderer *vr, transition_image_layout(cmd_buffer, texture->image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, - VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT); + VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT, + VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_IGNORED); result = vkEndCommandBuffer(cmd_buffer); check_vk_success(result, "vkEndCommandBuffer"); @@ -3887,7 +3893,8 @@ vulkan_renderer_create_renderbuffer(struct weston_output *output, transition_image_layout(cmd_buffer, im->image, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, - 0, VK_ACCESS_TRANSFER_WRITE_BIT); + 0, VK_ACCESS_TRANSFER_WRITE_BIT, + VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_IGNORED); // Wait here is bad, but this is only on renderbuffer creation vulkan_renderer_cmd_end_wait(vr, &cmd_buffer); From 74bae8e5a7d5932b1064e9ca971a0222a2cacf16 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Wed, 6 May 2026 16:12:05 +0100 Subject: [PATCH 5/6] vulkan-renderer: Fix layouts for QFOT in repaint When we're performing a queue-family ownership transfer to a foreign device, we need to transition to GENERAL (because we have no PRESENT_SRC when we're operating on dmabufs) from the colour attachment stage before display reads, and we need to transition back to OPTIMAL before the colour attachment stage. Signed-off-by: Daniel Stone --- libweston/renderer-vulkan/vulkan-renderer.c | 40 ++++++--------------- 1 file changed, 10 insertions(+), 30 deletions(-) diff --git a/libweston/renderer-vulkan/vulkan-renderer.c b/libweston/renderer-vulkan/vulkan-renderer.c index 76523b985..e464c5a80 100644 --- a/libweston/renderer-vulkan/vulkan-renderer.c +++ b/libweston/renderer-vulkan/vulkan-renderer.c @@ -405,30 +405,6 @@ static const struct vulkan_extension_table vulkan_device_ext_table[] = { }, }; -static void -transfer_image_queue_family(VkCommandBuffer cmd_buffer, VkImage image, - uint32_t src_index, uint32_t dst_index) -{ - const VkImageMemoryBarrier barrier = { - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, - .oldLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, - .newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, - .srcAccessMask = 0, - .dstAccessMask = 0, - .image = image, - .subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - .subresourceRange.layerCount = 1, - .subresourceRange.levelCount = 1, - .srcQueueFamilyIndex = src_index, - .dstQueueFamilyIndex = dst_index, - }; - - vkCmdPipelineBarrier(cmd_buffer, - VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, - VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, - 0, 0, NULL, 0, NULL, 1, &barrier); -} - static void transition_image_layout(VkCommandBuffer cmd_buffer, VkImage image, VkImageLayout old_layout, VkImageLayout new_layout, @@ -2493,9 +2469,11 @@ vulkan_renderer_repaint_output(struct weston_output *output, if (rb->dmabuf) { // Transfer ownership of the dmabuf to Vulkan assert(vulkan_device_has(vr, EXTENSION_EXT_QUEUE_FAMILY_FOREIGN)); - transfer_image_queue_family(cmd_buffer, im->image, - VK_QUEUE_FAMILY_FOREIGN_EXT, - vr->queue_family); + transition_image_layout(cmd_buffer, im->image, + VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, + VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, + VK_ACCESS_MEMORY_READ_BIT, VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, + VK_QUEUE_FAMILY_FOREIGN_EXT, vr->queue_family); } const struct weston_size *fb = &vo->fb_size; @@ -2536,9 +2514,11 @@ vulkan_renderer_repaint_output(struct weston_output *output, if (rb->dmabuf) { // Transfer ownership of the dmabuf to DRM assert(vulkan_device_has(vr, EXTENSION_EXT_QUEUE_FAMILY_FOREIGN)); - transfer_image_queue_family(cmd_buffer, im->image, - vr->queue_family, - VK_QUEUE_FAMILY_FOREIGN_EXT); + transition_image_layout(cmd_buffer, im->image, + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_GENERAL, + VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, + VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_MEMORY_READ_BIT, + vr->queue_family, VK_QUEUE_FAMILY_FOREIGN_EXT); } result = vkEndCommandBuffer(cmd_buffer); From 55ce229733955fdb792921eb53c87410785b62f5 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Wed, 6 May 2026 16:15:10 +0100 Subject: [PATCH 6/6] drm-backend: Use explicit synchronisation for Vulkan We can't rely on Vulkan implementations placing an implicit fence on our dmabuf, so let's do it ourselves. Signed-off-by: Daniel Stone --- libweston/backend-drm/drm-gbm.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/libweston/backend-drm/drm-gbm.c b/libweston/backend-drm/drm-gbm.c index f3caa035e..9a16bdfc3 100644 --- a/libweston/backend-drm/drm-gbm.c +++ b/libweston/backend-drm/drm-gbm.c @@ -816,13 +816,15 @@ struct drm_fb * drm_output_render_vulkan(struct drm_output_state *state, pixman_region32_t *damage) { struct drm_output *output = state->output; + struct drm_plane_state *pstate = + drm_output_state_get_plane(state, output->scanout_handle->plane); + struct weston_renderer *renderer = output->base.compositor->renderer; struct drm_device *device = output->device; struct linux_dmabuf_memory *dmabuf; struct drm_fb *ret; - output->base.compositor->renderer->repaint_output(&output->base, - damage, - output->renderbuffer[output->current_image]); + renderer->repaint_output(&output->base, damage, + output->renderbuffer[output->current_image]); dmabuf = output->linux_dmabuf_memory[output->current_image]; if (!dmabuf) { @@ -840,6 +842,13 @@ drm_output_render_vulkan(struct drm_output_state *state, pixman_region32_t *dama return NULL; } + pstate->in_fence_fd = renderer->vulkan->create_fence_fd(&output->base); + if (pstate->in_fence_fd < 1) { + weston_log("failed to get fence fd from rendering\n"); + drm_fb_unref(ret); + return NULL; + } + output->current_image = (output->current_image + 1) % ARRAY_LENGTH(output->renderbuffer); return ret;