From 3bbfae73ae0512b6135f7e1208c10cda54bae005 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Mon, 5 Aug 2024 20:51:15 +0200 Subject: [PATCH] render/vulkan: add support for explicit sync --- include/render/vulkan.h | 23 ++++-- render/vulkan/pass.c | 149 ++++++++++++++++++++++++++++++++++----- render/vulkan/renderer.c | 82 ++++++++++----------- render/vulkan/texture.c | 6 -- render/vulkan/vulkan.c | 1 + 5 files changed, 190 insertions(+), 71 deletions(-) diff --git a/include/render/vulkan.h b/include/render/vulkan.h index 29403f01f..7ad2f8339 100644 --- a/include/render/vulkan.h +++ b/include/render/vulkan.h @@ -40,6 +40,7 @@ struct wlr_vk_device { int drm_fd; + bool sync_file_import_export; bool implicit_sync_interop; bool sampler_ycbcr_conversion; @@ -253,6 +254,8 @@ struct wlr_vk_command_buffer { // For DMA-BUF implicit sync interop, may be NULL VkSemaphore binary_semaphore; + + struct wl_array wait_semaphores; // VkSemaphore }; #define VULKAN_COMMAND_BUFFERS_CAP 64 @@ -367,6 +370,13 @@ VkCommandBuffer vulkan_record_stage_cb(struct wlr_vk_renderer *renderer); // finished execution. bool vulkan_submit_stage_wait(struct wlr_vk_renderer *renderer); +struct wlr_vk_render_pass_texture { + struct wlr_vk_texture *texture; + + struct wlr_drm_syncobj_timeline *wait_timeline; + uint64_t wait_point; +}; + struct wlr_vk_render_pass { struct wlr_render_pass base; struct wlr_vk_renderer *renderer; @@ -378,6 +388,11 @@ struct wlr_vk_render_pass { bool failed; bool srgb_pathway; // if false, rendering via intermediate blending buffer struct wlr_color_transform *color_transform; + + struct wlr_drm_syncobj_timeline *signal_timeline; + uint64_t signal_point; + + struct wl_array textures; // struct wlr_vk_render_pass_texture }; struct wlr_vk_render_pass *vulkan_begin_render_pass(struct wlr_vk_renderer *renderer, @@ -419,8 +434,10 @@ bool vulkan_wait_command_buffer(struct wlr_vk_command_buffer *cb, struct wlr_vk_renderer *renderer); bool vulkan_sync_render_buffer(struct wlr_vk_renderer *renderer, - struct wlr_vk_render_buffer *render_buffer, struct wlr_vk_command_buffer *cb); -bool vulkan_sync_foreign_texture(struct wlr_vk_texture *texture); + struct wlr_vk_render_buffer *render_buffer, struct wlr_vk_command_buffer *cb, + struct wlr_drm_syncobj_timeline *signal_timeline, uint64_t signal_point); +bool vulkan_sync_foreign_texture(struct wlr_vk_texture *texture, + int sync_file_fds[static WLR_DMABUF_MAX_PLANES]); bool vulkan_read_pixels(struct wlr_vk_renderer *vk_renderer, VkFormat src_format, VkImage src_image, @@ -450,8 +467,6 @@ struct wlr_vk_texture { // If imported from a wlr_buffer struct wlr_buffer *buffer; struct wlr_addon buffer_addon; - // For DMA-BUF implicit sync interop - VkSemaphore foreign_semaphores[WLR_DMABUF_MAX_PLANES]; struct wl_list views; // struct wlr_vk_texture_ds.link }; diff --git a/render/vulkan/pass.c b/render/vulkan/pass.c index c5487cb34..8be1cc5d9 100644 --- a/render/vulkan/pass.c +++ b/render/vulkan/pass.c @@ -1,8 +1,10 @@ #include #include #include +#include #include #include +#include #include "render/color.h" #include "render/vulkan.h" @@ -80,11 +82,65 @@ static void mat3_to_mat4(const float mat3[9], float mat4[4][4]) { } static void render_pass_destroy(struct wlr_vk_render_pass *pass) { + struct wlr_vk_render_pass_texture *pass_texture; + wl_array_for_each(pass_texture, &pass->textures) { + wlr_drm_syncobj_timeline_unref(pass_texture->wait_timeline); + } + wlr_color_transform_unref(pass->color_transform); + wlr_drm_syncobj_timeline_unref(pass->signal_timeline); rect_union_finish(&pass->updated_region); + wl_array_release(&pass->textures); 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_submit(struct wlr_render_pass *wlr_pass) { struct wlr_vk_render_pass *pass = get_render_pass(wlr_pass); struct wlr_vk_renderer *renderer = pass->renderer; @@ -179,14 +235,15 @@ static bool render_pass_submit(struct wlr_render_pass *wlr_pass) { vkCmdEndRenderPass(render_cb->vk); - // insert acquire and release barriers for dmabuf-images - uint32_t barrier_count = wl_list_length(&renderer->foreign_textures) + 1; - render_wait = calloc(barrier_count * WLR_DMABUF_MAX_PLANES, sizeof(*render_wait)); + size_t pass_textures_len = pass->textures.size / sizeof(struct wlr_vk_render_pass_texture); + size_t render_wait_cap = pass_textures_len * WLR_DMABUF_MAX_PLANES; + render_wait = calloc(render_wait_cap, sizeof(*render_wait)); if (render_wait == NULL) { wlr_log_errno(WLR_ERROR, "Allocation failed"); goto error; } + uint32_t barrier_count = wl_list_length(&renderer->foreign_textures) + 1; VkImageMemoryBarrier *acquire_barriers = calloc(barrier_count, sizeof(*acquire_barriers)); VkImageMemoryBarrier *release_barriers = calloc(barrier_count, sizeof(*release_barriers)); if (acquire_barriers == NULL || release_barriers == NULL) { @@ -198,7 +255,6 @@ static bool render_pass_submit(struct wlr_render_pass *wlr_pass) { struct wlr_vk_texture *texture, *tmp_tex; size_t idx = 0; - uint32_t render_wait_len = 0; wl_list_for_each_safe(texture, tmp_tex, &renderer->foreign_textures, foreign_link) { if (!texture->transitioned) { texture->transitioned = true; @@ -236,23 +292,53 @@ static bool render_pass_submit(struct wlr_render_pass *wlr_pass) { ++idx; - if (!vulkan_sync_foreign_texture(texture)) { - wlr_log(WLR_ERROR, "Failed to wait for foreign texture DMA-BUF fence"); + wl_list_remove(&texture->foreign_link); + texture->owned = false; + } + + uint32_t render_wait_len = 0; + struct wlr_vk_render_pass_texture *pass_texture; + wl_array_for_each(pass_texture, &pass->textures) { + int sync_file_fds[WLR_DMABUF_MAX_PLANES]; + for (size_t i = 0; i < WLR_DMABUF_MAX_PLANES; i++) { + sync_file_fds[i] = -1; + } + + if (pass_texture->wait_timeline) { + int sync_file_fd = wlr_drm_syncobj_timeline_export_sync_file(pass_texture->wait_timeline, pass_texture->wait_point); + if (sync_file_fd < 0) { + wlr_log(WLR_ERROR, "Failed to export wait timeline point as sync_file"); + continue; + } + + sync_file_fds[0] = sync_file_fd; } else { - for (size_t i = 0; i < WLR_DMABUF_MAX_PLANES; i++) { - if (texture->foreign_semaphores[i] != VK_NULL_HANDLE) { - assert(render_wait_len < barrier_count * WLR_DMABUF_MAX_PLANES); - render_wait[render_wait_len++] = (VkSemaphoreSubmitInfoKHR){ - .sType = VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO_KHR, - .semaphore = texture->foreign_semaphores[i], - .stageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT_KHR, - }; - } + struct wlr_vk_texture *texture = pass_texture->texture; + if (!vulkan_sync_foreign_texture(texture, sync_file_fds)) { + wlr_log(WLR_ERROR, "Failed to wait for foreign texture DMA-BUF fence"); + continue; } } - wl_list_remove(&texture->foreign_link); - texture->owned = false; + for (size_t i = 0; i < WLR_DMABUF_MAX_PLANES; i++) { + if (sync_file_fds[i] < 0) { + continue; + } + + VkSemaphore sem = render_pass_wait_sync_file(pass, render_wait_len, sync_file_fds[i]); + if (sem == VK_NULL_HANDLE) { + close(sync_file_fds[i]); + continue; + } + + render_wait[render_wait_len] = (VkSemaphoreSubmitInfoKHR){ + .sType = VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO_KHR, + .semaphore = sem, + .stageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT_KHR, + }; + + render_wait_len++; + } } // also add acquire/release barriers for the current render buffer @@ -452,7 +538,8 @@ static bool render_pass_submit(struct wlr_render_pass *wlr_pass) { wl_list_insert(&stage_cb->stage_buffers, &stage_buf->link); } - if (!vulkan_sync_render_buffer(renderer, render_buffer, render_cb)) { + if (!vulkan_sync_render_buffer(renderer, render_buffer, render_cb, + pass->signal_timeline, pass->signal_point)) { wlr_log(WLR_ERROR, "Failed to sync render buffer"); } @@ -704,6 +791,28 @@ static void render_pass_add_texture(struct wlr_render_pass *wlr_pass, texture->last_used_cb = pass->command_buffer; pixman_region32_fini(&clip); + + if (texture->dmabuf_imported || (options != NULL && options->wait_timeline != NULL)) { + struct wlr_vk_render_pass_texture *pass_texture = + wl_array_add(&pass->textures, sizeof(*pass_texture)); + if (pass_texture == NULL) { + pass->failed = true; + return; + } + + struct wlr_drm_syncobj_timeline *wait_timeline = NULL; + uint64_t wait_point = 0; + if (options != NULL && options->wait_timeline != NULL) { + wait_timeline = wlr_drm_syncobj_timeline_ref(options->wait_timeline); + wait_point = options->wait_point; + } + + *pass_texture = (struct wlr_vk_render_pass_texture){ + .texture = texture, + .wait_timeline = wait_timeline, + .wait_point = wait_point, + }; + } } static const struct wlr_render_pass_impl render_pass_impl = { @@ -967,6 +1076,10 @@ struct wlr_vk_render_pass *vulkan_begin_render_pass(struct wlr_vk_renderer *rend if (options != NULL && options->color_transform != NULL) { pass->color_transform = wlr_color_transform_ref(options->color_transform); } + if (options != NULL && options->signal_timeline != NULL) { + pass->signal_timeline = wlr_drm_syncobj_timeline_ref(options->signal_timeline); + pass->signal_point = options->signal_point; + } rect_union_init(&pass->updated_region); diff --git a/render/vulkan/renderer.c b/render/vulkan/renderer.c index 327f3b465..a2fb867d6 100644 --- a/render/vulkan/renderer.c +++ b/render/vulkan/renderer.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -921,9 +922,9 @@ static struct wlr_vk_render_buffer *get_render_buffer( return buffer; } -bool vulkan_sync_foreign_texture(struct wlr_vk_texture *texture) { +bool vulkan_sync_foreign_texture(struct wlr_vk_texture *texture, + int sync_file_fds[static WLR_DMABUF_MAX_PLANES]) { struct wlr_vk_renderer *renderer = texture->renderer; - VkResult res; struct wlr_dmabuf_attributes dmabuf = {0}; if (!wlr_buffer_get_dmabuf(texture->buffer, &dmabuf)) { @@ -960,52 +961,22 @@ bool vulkan_sync_foreign_texture(struct wlr_vk_texture *texture) { return false; } - if (texture->foreign_semaphores[i] == VK_NULL_HANDLE) { - VkSemaphoreCreateInfo semaphore_info = { - .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, - }; - res = vkCreateSemaphore(renderer->dev->dev, &semaphore_info, NULL, - &texture->foreign_semaphores[i]); - if (res != VK_SUCCESS) { - close(sync_file_fd); - wlr_vk_error("vkCreateSemaphore", res); - return false; - } - } - - 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 = texture->foreign_semaphores[i], - .fd = sync_file_fd, - }; - res = renderer->dev->api.vkImportSemaphoreFdKHR(renderer->dev->dev, &import_info); - if (res != VK_SUCCESS) { - close(sync_file_fd); - wlr_vk_error("vkImportSemaphoreFdKHR", res); - return false; - } + sync_file_fds[i] = sync_file_fd; } return true; } bool vulkan_sync_render_buffer(struct wlr_vk_renderer *renderer, - struct wlr_vk_render_buffer *render_buffer, struct wlr_vk_command_buffer *cb) { + struct wlr_vk_render_buffer *render_buffer, struct wlr_vk_command_buffer *cb, + struct wlr_drm_syncobj_timeline *signal_timeline, uint64_t signal_point) { VkResult res; - if (!renderer->dev->implicit_sync_interop) { + if (!renderer->dev->implicit_sync_interop && signal_timeline == NULL) { // We have no choice but to block here sadly return vulkan_wait_command_buffer(cb, renderer); } - struct wlr_dmabuf_attributes dmabuf = {0}; - if (!wlr_buffer_get_dmabuf(render_buffer->wlr_buffer, &dmabuf)) { - wlr_log(WLR_ERROR, "wlr_buffer_get_dmabuf failed"); - return false; - } - // Note: vkGetSemaphoreFdKHR implicitly resets the semaphore const VkSemaphoreGetFdInfoKHR get_fence_fd_info = { .sType = VK_STRUCTURE_TYPE_SEMAPHORE_GET_FD_INFO_KHR, @@ -1020,17 +991,32 @@ bool vulkan_sync_render_buffer(struct wlr_vk_renderer *renderer, return false; } - for (int i = 0; i < dmabuf.n_planes; i++) { - if (!dmabuf_import_sync_file(dmabuf.fd[i], DMA_BUF_SYNC_WRITE, - sync_file_fd)) { - close(sync_file_fd); - return false; + bool ok = false; + if (signal_timeline != NULL) { + if (!wlr_drm_syncobj_timeline_import_sync_file(signal_timeline, + signal_point, sync_file_fd)) { + goto out; + } + } else { + struct wlr_dmabuf_attributes dmabuf = {0}; + if (!wlr_buffer_get_dmabuf(render_buffer->wlr_buffer, &dmabuf)) { + wlr_log(WLR_ERROR, "wlr_buffer_get_dmabuf failed"); + goto out; + } + + for (int i = 0; i < dmabuf.n_planes; i++) { + if (!dmabuf_import_sync_file(dmabuf.fd[i], DMA_BUF_SYNC_WRITE, + sync_file_fd)) { + goto out; + } } } - close(sync_file_fd); + ok = true; - return true; +out: + close(sync_file_fd); + return ok; } static const struct wlr_drm_format_set *vulkan_get_texture_formats( @@ -1073,6 +1059,11 @@ static void vulkan_destroy(struct wlr_renderer *wlr_renderer) { if (cb->binary_semaphore != VK_NULL_HANDLE) { vkDestroySemaphore(renderer->dev->dev, cb->binary_semaphore, NULL); } + VkSemaphore *sem_ptr; + wl_array_for_each(sem_ptr, &cb->wait_semaphores) { + vkDestroySemaphore(renderer->dev->dev, *sem_ptr, NULL); + } + wl_array_release(&cb->wait_semaphores); } // stage.cb automatically freed with command pool @@ -2436,6 +2427,11 @@ struct wlr_renderer *vulkan_renderer_create_for_device(struct wlr_vk_device *dev wl_list_init(&renderer->color_transforms); wl_list_init(&renderer->pipeline_layouts); + uint64_t cap_syncobj_timeline; + if (dev->drm_fd >= 0 && drmGetCap(dev->drm_fd, DRM_CAP_SYNCOBJ_TIMELINE, &cap_syncobj_timeline) == 0) { + renderer->wlr_renderer.features.timeline = dev->sync_file_import_export && cap_syncobj_timeline != 0; + } + if (!init_static_render_data(renderer)) { goto error; } diff --git a/render/vulkan/texture.c b/render/vulkan/texture.c index e3bd69793..61a6040ee 100644 --- a/render/vulkan/texture.c +++ b/render/vulkan/texture.c @@ -208,12 +208,6 @@ void vulkan_texture_destroy(struct wlr_vk_texture *texture) { free(view); } - for (size_t i = 0; i < WLR_DMABUF_MAX_PLANES; i++) { - if (texture->foreign_semaphores[i] != VK_NULL_HANDLE) { - vkDestroySemaphore(dev, texture->foreign_semaphores[i], NULL); - } - } - vkDestroyImage(dev, texture->image, NULL); for (unsigned i = 0u; i < texture->mem_count; ++i) { diff --git a/render/vulkan/vulkan.c b/render/vulkan/vulkan.c index 905feb906..00e5b4519 100644 --- a/render/vulkan/vulkan.c +++ b/render/vulkan/vulkan.c @@ -527,6 +527,7 @@ struct wlr_vk_device *vulkan_device_create(struct wlr_vk_instance *ini, wlr_log(WLR_DEBUG, "DMA-BUF sync_file import/export not supported"); } + dev->sync_file_import_export = exportable_semaphore && importable_semaphore; dev->implicit_sync_interop = exportable_semaphore && importable_semaphore && dmabuf_sync_file_import_export; if (dev->implicit_sync_interop) {