gpu: sync dmabufs via semaphores

This ensures both that we signal a semaphore for a dmabuf when we export
an image and that we import semaphores for dmabufs and wait on them.

Fixes Vulkan node-editor displaying the Vulkan renderer in the sidebar.
This commit is contained in:
Benjamin Otte
2023-11-29 23:14:00 +01:00
parent cfcc9658b2
commit d4c4e4bbc5
15 changed files with 206 additions and 3 deletions

View File

@@ -47,6 +47,8 @@ typedef enum {
GDK_VULKAN_FEATURE_DESCRIPTOR_INDEXING = 1 << 2,
GDK_VULKAN_FEATURE_DYNAMIC_INDEXING = 1 << 3,
GDK_VULKAN_FEATURE_NONUNIFORM_INDEXING = 1 << 4,
GDK_VULKAN_FEATURE_SEMAPHORE_EXPORT = 1 << 5,
GDK_VULKAN_FEATURE_SEMAPHORE_IMPORT = 1 << 6,
} GdkVulkanFeatures;
/* Tracks information about the device grab on this display */

View File

@@ -39,6 +39,8 @@ static const GdkDebugKey gsk_vulkan_feature_keys[] = {
{ "descriptor-indexing", GDK_VULKAN_FEATURE_DESCRIPTOR_INDEXING, "Force slow descriptor set layout codepath" },
{ "dynamic-indexing", GDK_VULKAN_FEATURE_DYNAMIC_INDEXING, "Hardcode small number of buffer and texure arrays" },
{ "nonuniform-indexing", GDK_VULKAN_FEATURE_NONUNIFORM_INDEXING, "Split draw calls to ensure uniform texture accesses" },
{ "semaphore-export", GDK_VULKAN_FEATURE_SEMAPHORE_EXPORT, "Disable sync of exported dmabufs" },
{ "semaphore-import", GDK_VULKAN_FEATURE_SEMAPHORE_IMPORT, "Disable sync of imported dmabufs" },
};
#endif
@@ -562,8 +564,17 @@ physical_device_check_features (VkPhysicalDevice device,
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2,
.pNext = &ycbcr_features
};
VkExternalSemaphoreProperties semaphore_props = {
.sType = VK_STRUCTURE_TYPE_EXTERNAL_SEMAPHORE_PROPERTIES,
};
vkGetPhysicalDeviceFeatures2 (device, &features);
vkGetPhysicalDeviceExternalSemaphoreProperties (device,
&(VkPhysicalDeviceExternalSemaphoreInfo) {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_SEMAPHORE_INFO,
.handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT,
},
&semaphore_props);
*out_features = 0;
@@ -591,6 +602,15 @@ physical_device_check_features (VkPhysicalDevice device,
physical_device_supports_extension (device, VK_EXT_IMAGE_DRM_FORMAT_MODIFIER_EXTENSION_NAME))
*out_features |= GDK_VULKAN_FEATURE_DMABUF;
if (physical_device_supports_extension (device, VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME))
{
if (semaphore_props.externalSemaphoreFeatures & VK_EXTERNAL_SEMAPHORE_FEATURE_EXPORTABLE_BIT)
*out_features |= GDK_VULKAN_FEATURE_SEMAPHORE_EXPORT;
if (semaphore_props.externalSemaphoreFeatures & VK_EXTERNAL_SEMAPHORE_FEATURE_IMPORTABLE_BIT)
*out_features |= GDK_VULKAN_FEATURE_SEMAPHORE_IMPORT;
}
return TRUE;
}
@@ -1450,6 +1470,8 @@ gdk_display_create_vulkan_device (GdkDisplay *display,
g_ptr_array_add (device_extensions, (gpointer) VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME);
g_ptr_array_add (device_extensions, (gpointer) VK_EXT_IMAGE_DRM_FORMAT_MODIFIER_EXTENSION_NAME);
}
if (features & (GDK_VULKAN_FEATURE_SEMAPHORE_IMPORT | GDK_VULKAN_FEATURE_SEMAPHORE_EXPORT))
g_ptr_array_add (device_extensions, (gpointer) VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME);
if (physical_device_supports_extension (devices[i], VK_KHR_INCREMENTAL_PRESENT_EXTENSION_NAME))
g_ptr_array_add (device_extensions, (gpointer) VK_KHR_INCREMENTAL_PRESENT_EXTENSION_NAME);

View File

@@ -59,6 +59,7 @@ gsk_gpu_blit_op_vk_command (GskGpuOp *op,
src_layout != VK_IMAGE_LAYOUT_GENERAL)
{
gsk_vulkan_image_transition (GSK_VULKAN_IMAGE (self->src_image),
state->semaphores,
state->vk_command_buffer,
VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
@@ -72,6 +73,7 @@ gsk_gpu_blit_op_vk_command (GskGpuOp *op,
dest_layout != VK_IMAGE_LAYOUT_GENERAL)
{
gsk_vulkan_image_transition (GSK_VULKAN_IMAGE (self->dest_image),
state->semaphores,
state->vk_command_buffer,
VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,

View File

@@ -8,11 +8,17 @@
#include "gskgpuprintprivate.h"
#ifdef GDK_RENDERING_VULKAN
#include "gskvulkanbufferprivate.h"
#include "gskvulkanframeprivate.h"
#include "gskvulkanimageprivate.h"
#endif
#include "gdk/gdkdmabuftextureprivate.h"
#include "gdk/gdkglcontextprivate.h"
#ifdef HAVE_DMABUF
#include <linux/dma-buf.h>
#endif
typedef struct _GskGpuDownloadOp GskGpuDownloadOp;
typedef void (* GdkGpuDownloadOpCreateFunc) (GskGpuDownloadOp *);
@@ -28,6 +34,9 @@ struct _GskGpuDownloadOp
GdkTexture *texture;
GskGpuBuffer *buffer;
#ifdef GDK_RENDERING_VULKAN
VkSemaphore vk_semaphore;
#endif
};
static void
@@ -59,6 +68,40 @@ gsk_gpu_download_op_print (GskGpuOp *op,
}
#ifdef GDK_RENDERING_VULKAN
/* The code needs to run here because vkGetSemaphoreFdKHR() may
* only be called after the semaphore has been submitted via
* vkQueueSubmit().
*/
static void
gsk_gpu_download_op_vk_sync_semaphore (GskGpuDownloadOp *self)
{
PFN_vkGetSemaphoreFdKHR func_vkGetSemaphoreFdKHR;
GdkDisplay *display;
int fd, sync_file_fd;
/* Don't look at where I store my variables plz */
display = gdk_dmabuf_texture_get_display (GDK_DMABUF_TEXTURE (self->texture));
fd = gdk_dmabuf_texture_get_dmabuf (GDK_DMABUF_TEXTURE (self->texture))->planes[0].fd;
func_vkGetSemaphoreFdKHR = (PFN_vkGetSemaphoreFdKHR) vkGetDeviceProcAddr (display->vk_device, "vkGetSemaphoreFdKHR");
/* vkGetSemaphoreFdKHR implicitly resets the semaphore.
* But who cares, we're about to delete it. */
if (GSK_VK_CHECK (func_vkGetSemaphoreFdKHR, display->vk_device,
&(VkSemaphoreGetFdInfoKHR) {
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_GET_FD_INFO_KHR,
.semaphore = self->vk_semaphore,
.handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT,
},
&sync_file_fd) == VK_SUCCESS)
{
gdk_dmabuf_import_sync_file (fd, DMA_BUF_SYNC_WRITE, sync_file_fd);
close (sync_file_fd);
}
vkDestroySemaphore (display->vk_device, self->vk_semaphore, NULL);
}
static void
gsk_gpu_download_op_vk_create (GskGpuDownloadOp *self)
{
@@ -93,7 +136,31 @@ gsk_gpu_download_op_vk_command (GskGpuOp *op,
#ifdef HAVE_DMABUF
self->texture = gsk_vulkan_image_to_dmabuf_texture (GSK_VULKAN_IMAGE (self->image));
if (self->texture)
return op->next;
{
GskVulkanDevice *device = GSK_VULKAN_DEVICE (gsk_gpu_frame_get_device (frame));
VkDevice vk_device = gsk_vulkan_device_get_vk_device (device);
gsk_gpu_device_cache_texture_image (GSK_GPU_DEVICE (device), self->texture, gsk_gpu_frame_get_timestamp (frame), self->image);
if (gsk_vulkan_device_has_feature (device, GDK_VULKAN_FEATURE_SEMAPHORE_EXPORT))
{
GSK_VK_CHECK (vkCreateSemaphore, vk_device,
&(VkSemaphoreCreateInfo) {
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
.pNext = &(VkExportSemaphoreCreateInfo) {
.sType = VK_STRUCTURE_TYPE_EXPORT_SEMAPHORE_CREATE_INFO,
.handleTypes = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT,
},
},
NULL,
&self->vk_semaphore);
gsk_vulkan_semaphores_add_signal (state->semaphores, self->vk_semaphore);
self->create_func = gsk_gpu_download_op_vk_sync_semaphore;
}
return op->next;
}
#endif
width = gsk_gpu_image_get_width (self->image);
@@ -103,6 +170,7 @@ gsk_gpu_download_op_vk_command (GskGpuOp *op,
height * stride);
gsk_vulkan_image_transition (GSK_VULKAN_IMAGE (self->image),
state->semaphores,
state->vk_command_buffer,
VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,

View File

@@ -58,6 +58,7 @@ gsk_gpu_mipmap_op_vk_command (GskGpuOp *op,
/* optimize me: only transition mipmap layers 1..n, but not 0 */
gsk_vulkan_image_transition (image,
state->semaphores,
state->vk_command_buffer,
VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,

View File

@@ -39,6 +39,7 @@ struct _GskVulkanCommandState
VkCommandBuffer vk_command_buffer;
GskVulkanDescriptors *desc;
GskVulkanSemaphores *semaphores;
};
#endif

View File

@@ -88,11 +88,11 @@ gsk_gpu_render_pass_op_do_barriers (GskGpuRenderPassOp *self,
state->desc = GSK_VULKAN_DESCRIPTORS (shader->desc);
}
desc = shader->desc;
gsk_vulkan_descriptors_transition (GSK_VULKAN_DESCRIPTORS (desc), state->vk_command_buffer);
gsk_vulkan_descriptors_transition (GSK_VULKAN_DESCRIPTORS (desc), state->semaphores, state->vk_command_buffer);
}
if (desc == NULL)
gsk_vulkan_descriptors_transition (state->desc, state->vk_command_buffer);
gsk_vulkan_descriptors_transition (state->desc, state->semaphores, state->vk_command_buffer);
}
static GskGpuOp *

View File

@@ -18,6 +18,7 @@ typedef struct _GskGpuOpClass GskGpuOpClass;
typedef struct _GskGpuShaderOp GskGpuShaderOp;
typedef struct _GskGpuShaderOpClass GskGpuShaderOpClass;
typedef struct _GskVulkanDescriptors GskVulkanDescriptors;
typedef struct _GskVulkanSemaphores GskVulkanSemaphores;
typedef enum {
GSK_GPU_IMAGE_EXTERNAL = (1 << 0),

View File

@@ -130,6 +130,7 @@ gsk_gpu_upload_op_vk_command_with_area (GskGpuOp *op,
},
0, NULL);
gsk_vulkan_image_transition (image,
state->semaphores,
state->vk_command_buffer,
VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,

View File

@@ -26,6 +26,7 @@ gsk_vulkan_descriptors_get_pipeline_layout (GskVulkanDescriptors *self)
void
gsk_vulkan_descriptors_transition (GskVulkanDescriptors *self,
GskVulkanSemaphores *semaphores,
VkCommandBuffer vk_command_buffer)
{
GskGpuDescriptors *desc = GSK_GPU_DESCRIPTORS (self);
@@ -34,6 +35,7 @@ gsk_vulkan_descriptors_transition (GskVulkanDescriptors *self,
for (i = 0; i < gsk_gpu_descriptors_get_n_images (desc); i++)
{
gsk_vulkan_image_transition (GSK_VULKAN_IMAGE (gsk_gpu_descriptors_get_image (desc, i)),
semaphores,
vk_command_buffer,
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,

View File

@@ -35,6 +35,7 @@ GType gsk_vulkan_descriptors_get_type
GskVulkanPipelineLayout * gsk_vulkan_descriptors_get_pipeline_layout (GskVulkanDescriptors *self);
void gsk_vulkan_descriptors_transition (GskVulkanDescriptors *self,
GskVulkanSemaphores *semaphores,
VkCommandBuffer vk_command_buffer);
void gsk_vulkan_descriptors_bind (GskVulkanDescriptors *self,
GskVulkanDescriptors *previous,

View File

@@ -20,6 +20,27 @@
#define GDK_ARRAY_NO_MEMSET 1
#include "gdk/gdkarrayimpl.c"
#define GDK_ARRAY_NAME gsk_semaphores
#define GDK_ARRAY_TYPE_NAME GskSemaphores
#define GDK_ARRAY_ELEMENT_TYPE VkSemaphore
#define GDK_ARRAY_PREALLOC 16
#define GDK_ARRAY_NO_MEMSET 1
#include "gdk/gdkarrayimpl.c"
#define GDK_ARRAY_NAME gsk_pipeline_stages
#define GDK_ARRAY_TYPE_NAME GskPipelineStages
#define GDK_ARRAY_ELEMENT_TYPE VkPipelineStageFlags
#define GDK_ARRAY_PREALLOC 16
#define GDK_ARRAY_NO_MEMSET 1
#include "gdk/gdkarrayimpl.c"
struct _GskVulkanSemaphores
{
GskSemaphores wait_semaphores;
GskPipelineStages wait_stages;
GskSemaphores signal_semaphores;
};
struct _GskVulkanFrame
{
GskGpuFrame parent_instance;
@@ -265,6 +286,7 @@ gsk_vulkan_frame_submit (GskGpuFrame *frame,
GskGpuOp *op)
{
GskVulkanFrame *self = GSK_VULKAN_FRAME (frame);
GskVulkanSemaphores semaphores;
GskVulkanCommandState state;
if (gsk_descriptors_get_size (&self->descriptors) == 0)
@@ -287,10 +309,16 @@ gsk_vulkan_frame_submit (GskGpuFrame *frame,
},
(VkDeviceSize[1]) { 0 });
gsk_semaphores_init (&semaphores.wait_semaphores);
gsk_pipeline_stages_init (&semaphores.wait_stages);
gsk_semaphores_init (&semaphores.signal_semaphores);
state.vk_command_buffer = self->vk_command_buffer;
state.vk_render_pass = VK_NULL_HANDLE;
state.vk_format = VK_FORMAT_UNDEFINED;
state.desc = GSK_VULKAN_DESCRIPTORS (gsk_descriptors_get (&self->descriptors, 0));
state.semaphores = &semaphores;
gsk_vulkan_descriptors_bind (GSK_VULKAN_DESCRIPTORS (gsk_descriptors_get (&self->descriptors, 0)),
NULL,
state.vk_command_buffer);
@@ -308,8 +336,17 @@ gsk_vulkan_frame_submit (GskGpuFrame *frame,
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
.commandBufferCount = 1,
.pCommandBuffers = &self->vk_command_buffer,
.pWaitSemaphores = gsk_semaphores_get_data (&semaphores.wait_semaphores),
.pWaitDstStageMask = gsk_pipeline_stages_get_data (&semaphores.wait_stages),
.waitSemaphoreCount = gsk_semaphores_get_size (&semaphores.wait_semaphores),
.pSignalSemaphores = gsk_semaphores_get_data (&semaphores.signal_semaphores),
.signalSemaphoreCount = gsk_semaphores_get_size (&semaphores.signal_semaphores),
},
self->vk_fence);
gsk_semaphores_clear (&semaphores.wait_semaphores);
gsk_pipeline_stages_clear (&semaphores.wait_stages);
gsk_semaphores_clear (&semaphores.signal_semaphores);
}
static void
@@ -376,3 +413,19 @@ gsk_vulkan_frame_get_vk_fence (GskVulkanFrame *self)
return self->vk_fence;
}
void
gsk_vulkan_semaphores_add_wait (GskVulkanSemaphores *self,
VkSemaphore semaphore,
VkPipelineStageFlags stage)
{
gsk_semaphores_append (&self->wait_semaphores, semaphore);
gsk_pipeline_stages_append (&self->wait_stages, stage);
}
void
gsk_vulkan_semaphores_add_signal (GskVulkanSemaphores *self,
VkSemaphore semaphore)
{
gsk_semaphores_append (&self->signal_semaphores, semaphore);
}

View File

@@ -12,5 +12,10 @@ G_DECLARE_FINAL_TYPE (GskVulkanFrame, gsk_vulkan_frame, GSK, VULKAN_FRAME, GskGp
VkFence gsk_vulkan_frame_get_vk_fence (GskVulkanFrame *self) G_GNUC_PURE;
void gsk_vulkan_semaphores_add_wait (GskVulkanSemaphores *self,
VkSemaphore semaphore,
VkPipelineStageFlags stage);
void gsk_vulkan_semaphores_add_signal (GskVulkanSemaphores *self,
VkSemaphore semaphore);
G_END_DECLS

View File

@@ -3,6 +3,7 @@
#include "gskvulkanimageprivate.h"
#include "gskvulkanbufferprivate.h"
#include "gskvulkanframeprivate.h"
#include "gskvulkanmemoryprivate.h"
#include "gdk/gdkdisplayprivate.h"
@@ -13,6 +14,9 @@
#include <fcntl.h>
#include <string.h>
#ifdef HAVE_DMABUF
#include <linux/dma-buf.h>
#endif
struct _GskVulkanImage
{
@@ -28,6 +32,7 @@ struct _GskVulkanImage
VkFramebuffer vk_framebuffer;
VkImageView vk_framebuffer_image_view;
VkSampler vk_sampler;
VkSemaphore vk_semaphore;
VkPipelineStageFlags vk_pipeline_stage;
VkImageLayout vk_image_layout;
@@ -1221,6 +1226,32 @@ gsk_vulkan_image_new_for_dmabuf (GskVulkanDevice *device,
},
&requirements);
if (gsk_vulkan_device_has_feature (device, GDK_VULKAN_FEATURE_SEMAPHORE_IMPORT))
{
int sync_file_fd = gdk_dmabuf_export_sync_file (fd, DMA_BUF_SYNC_READ);
if (sync_file_fd >= 0)
{
PFN_vkImportSemaphoreFdKHR func_vkImportSemaphoreFdKHR;
func_vkImportSemaphoreFdKHR = (PFN_vkImportSemaphoreFdKHR) vkGetDeviceProcAddr (vk_device, "vkImportSemaphoreFdKHR");
GSK_VK_CHECK (vkCreateSemaphore, vk_device,
&(VkSemaphoreCreateInfo) {
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
},
NULL,
&self->vk_semaphore);
GSK_VK_CHECK (func_vkImportSemaphoreFdKHR, vk_device,
&(VkImportSemaphoreFdInfoKHR) {
.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 = self->vk_semaphore,
.fd = sync_file_fd,
});
}
}
gsk_vulkan_alloc (self->allocator,
requirements.memoryRequirements.size,
requirements.memoryRequirements.alignment,
@@ -1440,6 +1471,8 @@ gsk_vulkan_image_to_dmabuf_texture (GskVulkanImage *self)
return NULL;
}
gsk_gpu_image_toggle_ref_texture (GSK_GPU_IMAGE (self), texture);
return texture;
}
#endif
@@ -1481,6 +1514,9 @@ gsk_vulkan_image_finalize (GObject *object)
if (self->vk_image_view != VK_NULL_HANDLE)
vkDestroyImageView (device, self->vk_image_view, NULL);
if (self->vk_semaphore != VK_NULL_HANDLE)
vkDestroySemaphore (device, self->vk_semaphore, NULL);
/* memory is NULL for for_swapchain() images, where we don't own
* the VkImage */
if (self->allocator)
@@ -1610,6 +1646,7 @@ gsk_vulkan_image_set_vk_image_layout (GskVulkanImage *self,
void
gsk_vulkan_image_transition (GskVulkanImage *self,
GskVulkanSemaphores *semaphores,
VkCommandBuffer command_buffer,
VkPipelineStageFlags stage,
VkImageLayout image_layout,
@@ -1620,6 +1657,12 @@ gsk_vulkan_image_transition (GskVulkanImage *self,
self->vk_access == access)
return;
if (self->vk_pipeline_stage == VK_IMAGE_LAYOUT_UNDEFINED &&
self->vk_semaphore)
{
gsk_vulkan_semaphores_add_wait (semaphores, self->vk_semaphore, stage);
}
vkCmdPipelineBarrier (command_buffer,
self->vk_pipeline_stage,
stage,

View File

@@ -52,6 +52,7 @@ void gsk_vulkan_image_set_vk_image_layout (GskVulk
VkImageLayout image_layout,
VkAccessFlags access);
void gsk_vulkan_image_transition (GskVulkanImage *self,
GskVulkanSemaphores *semaphores,
VkCommandBuffer command_buffer,
VkPipelineStageFlags stage,
VkImageLayout image_layout,