render: explicit sync for wlr_texture_read_pixels()

This commit is contained in:
Félix Poisot 2026-03-26 12:25:07 +00:00
parent 413664e0b0
commit f295d0322a
6 changed files with 110 additions and 55 deletions

View file

@ -410,7 +410,7 @@ VkCommandBuffer vulkan_record_stage_cb(struct wlr_vk_renderer *renderer);
// Submits the current stage command buffer and waits until it has
// finished execution.
bool vulkan_submit_stage_wait(struct wlr_vk_renderer *renderer);
bool vulkan_submit_stage_wait(struct wlr_vk_renderer *renderer, int wait_sync_file_fd);
struct wlr_vk_render_pass_texture {
struct wlr_vk_texture *texture;
@ -476,6 +476,8 @@ uint64_t vulkan_end_command_buffer(struct wlr_vk_command_buffer *cb,
void vulkan_reset_command_buffer(struct wlr_vk_command_buffer *cb);
bool vulkan_wait_command_buffer(struct wlr_vk_command_buffer *cb,
struct wlr_vk_renderer *renderer);
VkSemaphore vulkan_command_buffer_wait_sync_file(struct wlr_vk_renderer *renderer,
struct wlr_vk_command_buffer *render_cb, size_t sem_index, int sync_file_fd);
bool vulkan_sync_render_pass_release(struct wlr_vk_renderer *renderer,
struct wlr_vk_render_pass *pass);
@ -488,7 +490,8 @@ bool vulkan_read_pixels(struct wlr_vk_renderer *vk_renderer,
VkFormat src_format, VkImage src_image,
uint32_t drm_format, uint32_t stride,
uint32_t width, uint32_t height, uint32_t src_x, uint32_t src_y,
uint32_t dst_x, uint32_t dst_y, void *data);
uint32_t dst_x, uint32_t dst_y, void *data,
struct wlr_drm_syncobj_timeline *wait_timeline, uint64_t wait_point);
// State (e.g. image texture) associated with a surface.
struct wlr_vk_texture {

View file

@ -37,6 +37,8 @@ struct wlr_texture_read_pixels_options {
uint32_t dst_x, dst_y;
/** Source box of the texture to read from. If empty, the full texture is assumed. */
const struct wlr_box src_box;
struct wlr_drm_syncobj_timeline *wait_timeline;
uint64_t wait_point;
};
bool wlr_texture_read_pixels(struct wlr_texture *texture,

View file

@ -4,8 +4,10 @@
#include <GLES2/gl2ext.h>
#include <stdint.h>
#include <stdlib.h>
#include <unistd.h>
#include <wayland-server-protocol.h>
#include <wayland-util.h>
#include <wlr/render/drm_syncobj.h>
#include <wlr/render/egl.h>
#include <wlr/render/interface.h>
#include <wlr/render/wlr_texture.h>
@ -201,6 +203,27 @@ static bool gles2_texture_read_pixels(struct wlr_texture *wlr_texture,
return false;
}
if (options->wait_timeline != NULL) {
int sync_file_fd =
wlr_drm_syncobj_timeline_export_sync_file(options->wait_timeline, options->wait_point);
if (sync_file_fd < 0) {
return false;
}
struct wlr_gles2_renderer *renderer = texture->renderer;
EGLSyncKHR sync = wlr_egl_create_sync(renderer->egl, sync_file_fd);
close(sync_file_fd);
if (sync == EGL_NO_SYNC_KHR) {
return false;
}
bool ok = wlr_egl_wait_sync(renderer->egl, sync);
wlr_egl_destroy_sync(renderer->egl, sync);
if (!ok) {
return false;
}
}
// Make sure any pending drawing is finished before we try to read it
glFinish();

View file

@ -99,53 +99,6 @@ static void render_pass_destroy(struct wlr_vk_render_pass *pass) {
free(pass);
}
static VkSemaphore render_pass_wait_sync_file(struct wlr_vk_render_pass *pass,
size_t sem_index, int sync_file_fd) {
struct wlr_vk_renderer *renderer = pass->renderer;
struct wlr_vk_command_buffer *render_cb = pass->command_buffer;
VkResult res;
VkSemaphore *wait_semaphores = render_cb->wait_semaphores.data;
size_t wait_semaphores_len = render_cb->wait_semaphores.size / sizeof(wait_semaphores[0]);
VkSemaphore *sem_ptr;
if (sem_index >= wait_semaphores_len) {
sem_ptr = wl_array_add(&render_cb->wait_semaphores, sizeof(*sem_ptr));
if (sem_ptr == NULL) {
return VK_NULL_HANDLE;
}
*sem_ptr = VK_NULL_HANDLE;
} else {
sem_ptr = &wait_semaphores[sem_index];
}
if (*sem_ptr == VK_NULL_HANDLE) {
VkSemaphoreCreateInfo semaphore_info = {
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
};
res = vkCreateSemaphore(renderer->dev->dev, &semaphore_info, NULL, sem_ptr);
if (res != VK_SUCCESS) {
wlr_vk_error("vkCreateSemaphore", res);
return VK_NULL_HANDLE;
}
}
VkImportSemaphoreFdInfoKHR import_info = {
.sType = VK_STRUCTURE_TYPE_IMPORT_SEMAPHORE_FD_INFO_KHR,
.handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT,
.flags = VK_SEMAPHORE_IMPORT_TEMPORARY_BIT,
.semaphore = *sem_ptr,
.fd = sync_file_fd,
};
res = renderer->dev->api.vkImportSemaphoreFdKHR(renderer->dev->dev, &import_info);
if (res != VK_SUCCESS) {
wlr_vk_error("vkImportSemaphoreFdKHR", res);
return VK_NULL_HANDLE;
}
return *sem_ptr;
}
static bool render_pass_wait_render_buffer(struct wlr_vk_render_pass *pass,
VkSemaphoreSubmitInfoKHR *render_wait, uint32_t *render_wait_len_ptr) {
int sync_file_fds[WLR_DMABUF_MAX_PLANES];
@ -162,7 +115,8 @@ static bool render_pass_wait_render_buffer(struct wlr_vk_render_pass *pass,
continue;
}
VkSemaphore sem = render_pass_wait_sync_file(pass, *render_wait_len_ptr, sync_file_fds[i]);
VkSemaphore sem = vulkan_command_buffer_wait_sync_file(pass->renderer,
pass->command_buffer, *render_wait_len_ptr, sync_file_fds[i]);
if (sem == VK_NULL_HANDLE) {
close(sync_file_fds[i]);
continue;
@ -431,7 +385,8 @@ static bool render_pass_submit(struct wlr_render_pass *wlr_pass) {
continue;
}
VkSemaphore sem = render_pass_wait_sync_file(pass, render_wait_len, sync_file_fds[i]);
VkSemaphore sem = vulkan_command_buffer_wait_sync_file(renderer, render_cb,
render_wait_len, sync_file_fds[i]);
if (sem == VK_NULL_HANDLE) {
close(sync_file_fds[i]);
continue;

View file

@ -379,7 +379,52 @@ VkCommandBuffer vulkan_record_stage_cb(struct wlr_vk_renderer *renderer) {
return renderer->stage.cb->vk;
}
bool vulkan_submit_stage_wait(struct wlr_vk_renderer *renderer) {
VkSemaphore vulkan_command_buffer_wait_sync_file(struct wlr_vk_renderer *renderer,
struct wlr_vk_command_buffer *render_cb, size_t sem_index, int sync_file_fd) {
VkResult res;
VkSemaphore *wait_semaphores = render_cb->wait_semaphores.data;
size_t wait_semaphores_len = render_cb->wait_semaphores.size / sizeof(wait_semaphores[0]);
VkSemaphore *sem_ptr;
if (sem_index >= wait_semaphores_len) {
sem_ptr = wl_array_add(&render_cb->wait_semaphores, sizeof(*sem_ptr));
if (sem_ptr == NULL) {
return VK_NULL_HANDLE;
}
*sem_ptr = VK_NULL_HANDLE;
} else {
sem_ptr = &wait_semaphores[sem_index];
}
if (*sem_ptr == VK_NULL_HANDLE) {
VkSemaphoreCreateInfo semaphore_info = {
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
};
res = vkCreateSemaphore(renderer->dev->dev, &semaphore_info, NULL, sem_ptr);
if (res != VK_SUCCESS) {
wlr_vk_error("vkCreateSemaphore", res);
return VK_NULL_HANDLE;
}
}
VkImportSemaphoreFdInfoKHR import_info = {
.sType = VK_STRUCTURE_TYPE_IMPORT_SEMAPHORE_FD_INFO_KHR,
.handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT,
.flags = VK_SEMAPHORE_IMPORT_TEMPORARY_BIT,
.semaphore = *sem_ptr,
.fd = sync_file_fd,
};
res = renderer->dev->api.vkImportSemaphoreFdKHR(renderer->dev->dev, &import_info);
if (res != VK_SUCCESS) {
wlr_vk_error("vkImportSemaphoreFdKHR", res);
return VK_NULL_HANDLE;
}
return *sem_ptr;
}
bool vulkan_submit_stage_wait(struct wlr_vk_renderer *renderer, int wait_sync_file_fd) {
if (renderer->stage.cb == NULL) {
return false;
}
@ -389,9 +434,12 @@ bool vulkan_submit_stage_wait(struct wlr_vk_renderer *renderer) {
uint64_t timeline_point = vulkan_end_command_buffer(cb, renderer);
if (timeline_point == 0) {
close(wait_sync_file_fd);
return false;
}
VkSemaphore wait_semaphore;
VkPipelineStageFlags wait_stage = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT;
VkTimelineSemaphoreSubmitInfoKHR timeline_submit_info = {
.sType = VK_STRUCTURE_TYPE_TIMELINE_SEMAPHORE_SUBMIT_INFO_KHR,
.signalSemaphoreValueCount = 1,
@ -405,6 +453,18 @@ bool vulkan_submit_stage_wait(struct wlr_vk_renderer *renderer) {
.signalSemaphoreCount = 1,
.pSignalSemaphores = &renderer->timeline_semaphore,
};
if (wait_sync_file_fd != -1) {
wait_semaphore = vulkan_command_buffer_wait_sync_file(renderer, cb, 0, wait_sync_file_fd);
if (wait_semaphore == VK_NULL_HANDLE) {
close(wait_sync_file_fd);
return false;
}
submit_info.waitSemaphoreCount = 1;
submit_info.pWaitSemaphores = &wait_semaphore;
submit_info.pWaitDstStageMask = &wait_stage;
}
VkResult res = vkQueueSubmit(renderer->dev->queue, 1, &submit_info, VK_NULL_HANDLE);
if (res != VK_SUCCESS) {
wlr_vk_error("vkQueueSubmit", res);
@ -1218,7 +1278,8 @@ bool vulkan_read_pixels(struct wlr_vk_renderer *vk_renderer,
VkFormat src_format, VkImage src_image,
uint32_t drm_format, uint32_t stride,
uint32_t width, uint32_t height, uint32_t src_x, uint32_t src_y,
uint32_t dst_x, uint32_t dst_y, void *data) {
uint32_t dst_x, uint32_t dst_y, void *data,
struct wlr_drm_syncobj_timeline *wait_timeline, uint64_t wait_point) {
VkDevice dev = vk_renderer->dev->dev;
const struct wlr_pixel_format_info *pixel_format_info = drm_get_pixel_format_info(drm_format);
@ -1404,7 +1465,17 @@ bool vulkan_read_pixels(struct wlr_vk_renderer *vk_renderer,
VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_ACCESS_MEMORY_READ_BIT);
if (!vulkan_submit_stage_wait(vk_renderer)) {
int wait_sync_file_fd = -1;
if (wait_timeline != NULL) {
wait_sync_file_fd = wlr_drm_syncobj_timeline_export_sync_file(wait_timeline, wait_point);
if (wait_sync_file_fd < 0) {
wlr_log(WLR_ERROR, "Failed to export wait timeline point as sync_file");
return false;
}
}
if (!vulkan_submit_stage_wait(vk_renderer, wait_sync_file_fd)) {
close(wait_sync_file_fd);
return false;
}

View file

@ -238,7 +238,8 @@ static bool vulkan_texture_read_pixels(struct wlr_texture *wlr_texture,
void *p = wlr_texture_read_pixel_options_get_data(options);
return vulkan_read_pixels(texture->renderer, texture->format->vk, texture->image,
options->format, options->stride, src.width, src.height, src.x, src.y, 0, 0, p);
options->format, options->stride, src.width, src.height, src.x, src.y, 0, 0, p,
options->wait_timeline, options->wait_point);
}
static uint32_t vulkan_texture_preferred_read_format(struct wlr_texture *wlr_texture) {