render/vulkan: add support for explicit sync

This commit is contained in:
Simon Ser 2024-08-05 20:51:15 +02:00 committed by Alexander Orzechowski
parent 9351c78d70
commit 3bbfae73ae
5 changed files with 190 additions and 71 deletions

View File

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

View File

@ -1,8 +1,10 @@
#include <assert.h>
#include <drm_fourcc.h>
#include <stdlib.h>
#include <unistd.h>
#include <wlr/util/log.h>
#include <wlr/render/color.h>
#include <wlr/render/drm_syncobj.h>
#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");
} 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,
};
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 {
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);

View File

@ -13,6 +13,7 @@
#include <wlr/types/wlr_drm.h>
#include <wlr/util/box.h>
#include <wlr/util/log.h>
#include <wlr/render/drm_syncobj.h>
#include <wlr/render/vulkan.h>
#include <wlr/backend/interface.h>
#include <wlr/types/wlr_linux_dmabuf_v1.h>
@ -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;
}
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)) {
close(sync_file_fd);
return false;
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;
}

View File

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

View File

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