aboutsummaryrefslogtreecommitdiff
path: root/render/vulkan/renderer.c
diff options
context:
space:
mode:
authorSimon Ser <contact@emersion.fr>2022-12-02 19:15:16 +0100
committerSimon Zeni <simon@bl4ckb0ne.ca>2022-12-06 14:54:09 +0000
commit8456ac6fa9dadadca0b69ee0a0b02e60fbd45427 (patch)
tree26cb058ffbf6c92136fc721bec0bcd5feed0ee5e /render/vulkan/renderer.c
parent30219cf76b0c497783a3d04e1613504468e49dc7 (diff)
render/vulkan: wait for DMA-BUF fences
The Vulkan spec doesn't guarantee that the driver will wait for implicitly synchronized client buffers before texturing from them. radv happens to perform the wait, but anv doesn't. Fix this by extracting implicit fences from DMA-BUFs, importing them into Vulkan as a VkSemaphore objects, and make the render pass wait on these VkSemaphores.
Diffstat (limited to 'render/vulkan/renderer.c')
-rw-r--r--render/vulkan/renderer.c105
1 files changed, 103 insertions, 2 deletions
diff --git a/render/vulkan/renderer.c b/render/vulkan/renderer.c
index c780770c..dfdc13cd 100644
--- a/render/vulkan/renderer.c
+++ b/render/vulkan/renderer.c
@@ -2,6 +2,7 @@
#include <assert.h>
#include <fcntl.h>
#include <math.h>
+#include <poll.h>
#include <stdlib.h>
#include <stdint.h>
#include <sys/types.h>
@@ -772,6 +773,76 @@ static bool vulkan_begin(struct wlr_renderer *wlr_renderer,
return true;
}
+static bool vulkan_sync_foreign_texture(struct wlr_vk_texture *texture) {
+ struct wlr_vk_renderer *renderer = texture->renderer;
+ VkResult res;
+
+ struct wlr_dmabuf_attributes dmabuf = {0};
+ if (!wlr_buffer_get_dmabuf(texture->buffer, &dmabuf)) {
+ wlr_log(WLR_ERROR, "Failed to get texture DMA-BUF");
+ return false;
+ }
+
+ if (!renderer->dev->implicit_sync_interop) {
+ // We have no choice but to block here sadly
+
+ for (int i = 0; i < dmabuf.n_planes; i++) {
+ struct pollfd pollfd = {
+ .fd = dmabuf.fd[i],
+ .events = POLLIN,
+ };
+ int timeout_ms = 1000;
+ int ret = poll(&pollfd, 1, timeout_ms);
+ if (ret < 0) {
+ wlr_log_errno(WLR_ERROR, "Failed to wait for DMA-BUF fence");
+ return false;
+ } else if (ret == 0) {
+ wlr_log(WLR_ERROR, "Timed out while waiting for DMA-BUF fence");
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ for (int i = 0; i < dmabuf.n_planes; i++) {
+ int sync_file_fd = dmabuf_export_sync_file(dmabuf.fd[i], DMA_BUF_SYNC_READ);
+ if (sync_file_fd < 0) {
+ wlr_log(WLR_ERROR, "Failed to extract DMA-BUF fence");
+ 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.importSemaphoreFdKHR(renderer->dev->dev, &import_info);
+ close(sync_file_fd);
+ if (res != VK_SUCCESS) {
+ wlr_vk_error("vkImportSemaphoreFdKHR", res);
+ return false;
+ }
+ }
+
+ return true;
+}
+
static bool vulkan_sync_render_buffer(struct wlr_vk_renderer *renderer,
struct wlr_vk_command_buffer *cb) {
VkResult res;
@@ -841,16 +912,18 @@ static void vulkan_end(struct wlr_renderer *wlr_renderer) {
unsigned barrier_count = wl_list_length(&renderer->foreign_textures) + 1;
VkImageMemoryBarrier *acquire_barriers = calloc(barrier_count, sizeof(VkImageMemoryBarrier));
VkImageMemoryBarrier *release_barriers = calloc(barrier_count, sizeof(VkImageMemoryBarrier));
- if (acquire_barriers == NULL || release_barriers == NULL) {
+ VkSemaphore *render_wait = calloc(barrier_count * WLR_DMABUF_MAX_PLANES, sizeof(VkSemaphore));
+ if (acquire_barriers == NULL || release_barriers == NULL || render_wait == NULL) {
wlr_log_errno(WLR_ERROR, "Allocation failed");
free(acquire_barriers);
free(release_barriers);
+ free(render_wait);
return;
}
struct wlr_vk_texture *texture, *tmp_tex;
unsigned idx = 0;
-
+ uint32_t render_wait_len = 0;
wl_list_for_each_safe(texture, tmp_tex, &renderer->foreign_textures, foreign_link) {
VkImageLayout src_layout = VK_IMAGE_LAYOUT_GENERAL;
if (!texture->transitioned) {
@@ -890,6 +963,17 @@ static void vulkan_end(struct wlr_renderer *wlr_renderer) {
++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++] = texture->foreign_semaphores[i];
+ }
+ }
+ }
+
wl_list_remove(&texture->foreign_link);
texture->owned = false;
}
@@ -950,6 +1034,18 @@ static void vulkan_end(struct wlr_renderer *wlr_renderer) {
VkSubmitInfo *stage_sub = &submit_infos[0];
VkSubmitInfo *render_sub = &submit_infos[1];
+ VkPipelineStageFlags *render_wait_stages = NULL;
+ if (render_wait_len > 0) {
+ render_wait_stages = calloc(render_wait_len, sizeof(VkPipelineStageFlags));
+ if (render_wait_stages == NULL) {
+ wlr_log(WLR_ERROR, "Allocation failed");
+ return;
+ }
+ for (size_t i = 0; i < render_wait_len; i++) {
+ render_wait_stages[i] = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT;
+ }
+ }
+
// No semaphores needed here.
// We don't need a semaphore from the stage/transfer submission
// to the render submissions since they are on the same queue
@@ -1027,6 +1123,9 @@ static void vulkan_end(struct wlr_renderer *wlr_renderer) {
.pNext = &render_timeline_submit_info,
.pCommandBuffers = &render_cb->vk,
.commandBufferCount = 1,
+ .waitSemaphoreCount = render_wait_len,
+ .pWaitSemaphores = render_wait,
+ .pWaitDstStageMask = render_wait_stages,
.signalSemaphoreCount = render_signal_len,
.pSignalSemaphores = render_signal,
};
@@ -1042,6 +1141,8 @@ static void vulkan_end(struct wlr_renderer *wlr_renderer) {
return;
}
+ free(render_wait);
+
struct wlr_vk_shared_buffer *stage_buf, *stage_buf_tmp;
wl_list_for_each_safe(stage_buf, stage_buf_tmp, &renderer->stage.buffers, link) {
if (stage_buf->allocs.size == 0) {