From 496ecd68f214c00ed832acd6b311a7f507438d14 Mon Sep 17 00:00:00 2001 From: Benjamin Otte Date: Fri, 24 Nov 2023 07:10:34 +0100 Subject: [PATCH] gpu: Make Vulkan renderer provide dmabuf textures Make gsk_renderer_render_texture() create a dmabuf texture if that is possible. If it isn't (ie if we're not on Linux or if dmabufs are otherwise not working) fall back to the previous code of creating a memory texture. --- gsk/gpu/gskgpudownloadop.c | 6 + gsk/gpu/gskvulkandevice.c | 9 + gsk/gpu/gskvulkanimage.c | 313 ++++++++++++++++++++++++++++++++ gsk/gpu/gskvulkanimageprivate.h | 5 + 4 files changed, 333 insertions(+) diff --git a/gsk/gpu/gskgpudownloadop.c b/gsk/gpu/gskgpudownloadop.c index 50e825ed5e..bbb5a0a68e 100644 --- a/gsk/gpu/gskgpudownloadop.c +++ b/gsk/gpu/gskgpudownloadop.c @@ -90,6 +90,12 @@ gsk_gpu_download_op_vk_command (GskGpuOp *op, GskGpuDownloadOp *self = (GskGpuDownloadOp *) op; gsize width, height, stride; +#ifdef HAVE_DMABUF + self->texture = gsk_vulkan_image_to_dmabuf_texture (GSK_VULKAN_IMAGE (self->image)); + if (self->texture) + return op->next; +#endif + width = gsk_gpu_image_get_width (self->image); height = gsk_gpu_image_get_height (self->image); stride = width * gdk_memory_format_bytes_per_pixel (gsk_gpu_image_get_format (self->image)); diff --git a/gsk/gpu/gskvulkandevice.c b/gsk/gpu/gskvulkandevice.c index 7dab7963c1..4b7531c822 100644 --- a/gsk/gpu/gskvulkandevice.c +++ b/gsk/gpu/gskvulkandevice.c @@ -401,6 +401,15 @@ gsk_vulkan_device_create_download_image (GskGpuDevice *device, GskVulkanDevice *self = GSK_VULKAN_DEVICE (device); GskGpuImage *image; +#ifdef HAVE_DMABUF + image = gsk_vulkan_image_new_dmabuf (self, + gdk_memory_depth_get_format (depth), + width, + height); + if (image != NULL) + return image; +#endif + image = gsk_vulkan_image_new_for_offscreen (self, FALSE, gdk_memory_depth_get_format (depth), diff --git a/gsk/gpu/gskvulkanimage.c b/gsk/gpu/gskvulkanimage.c index 104cc6a60e..dcff48054e 100644 --- a/gsk/gpu/gskvulkanimage.c +++ b/gsk/gpu/gskvulkanimage.c @@ -862,6 +862,197 @@ gsk_vulkan_image_new_for_offscreen (GskVulkanDevice *device, } #ifdef HAVE_DMABUF +GskGpuImage * +gsk_vulkan_image_new_dmabuf (GskVulkanDevice *device, + GdkMemoryFormat format, + gsize width, + gsize height) +{ + VkDrmFormatModifierPropertiesEXT drm_mod_properties[100]; + VkDrmFormatModifierPropertiesListEXT drm_properties; + uint64_t modifiers[100]; + VkPhysicalDevice vk_phys_device; + VkDevice vk_device; + VkFormatProperties2 properties; + VkImageFormatProperties2 image_properties; + VkFormatFeatureFlags required; + const GskMemoryFormatInfo *format_info; + VkMemoryRequirements requirements; + GskVulkanImage *self; + VkResult res; + gsize i, n_modifiers; + gboolean can_blit; + + if (!gsk_vulkan_device_has_feature (device, GDK_VULKAN_FEATURE_DMABUF)) + return NULL; + + vk_phys_device = gsk_vulkan_device_get_vk_physical_device (device); + vk_device = gsk_vulkan_device_get_vk_device (device); + + drm_properties = (VkDrmFormatModifierPropertiesListEXT) { + .sType = VK_STRUCTURE_TYPE_DRM_FORMAT_MODIFIER_PROPERTIES_LIST_EXT, + .drmFormatModifierCount = G_N_ELEMENTS (drm_mod_properties), + .pDrmFormatModifierProperties = drm_mod_properties, + }; + properties = (VkFormatProperties2) { + .sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2, + .pNext = &drm_properties + }; + + required = VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT; + + while (TRUE) + { + for (format_info = gsk_memory_format_get_vk_format_infos (format); + format_info != VK_FORMAT_UNDEFINED; + format_info++) + { + if (!gsk_memory_format_info_is_framebuffer_compatible (format_info)) + continue; + + vkGetPhysicalDeviceFormatProperties2 (vk_phys_device, + format_info->format, + &properties); + + can_blit = TRUE; + n_modifiers = 0; + for (i = 0; i < drm_properties.drmFormatModifierCount; i++) + { + if ((drm_mod_properties[i].drmFormatModifierTilingFeatures & required) != required) + continue; + + image_properties = (VkImageFormatProperties2) { + .sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2, + }; + res = vkGetPhysicalDeviceImageFormatProperties2 (vk_phys_device, + &(VkPhysicalDeviceImageFormatInfo2) { + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2, + .format = format_info->format, + .type = VK_IMAGE_TYPE_2D, + .tiling = VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT, + .usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, + .flags = 0, + .pNext = &(VkPhysicalDeviceImageDrmFormatModifierInfoEXT) { + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_DRM_FORMAT_MODIFIER_INFO_EXT, + .drmFormatModifier = drm_mod_properties[i].drmFormatModifier, + .sharingMode = VK_SHARING_MODE_EXCLUSIVE, + .queueFamilyIndexCount = 1, + .pQueueFamilyIndices = (uint32_t[1]) { gsk_vulkan_device_get_vk_queue_family_index (device) }, + } + }, + &image_properties); + if (res != VK_SUCCESS) + continue; + + if (image_properties.imageFormatProperties.maxExtent.width < width || + image_properties.imageFormatProperties.maxExtent.height < height) + continue; + + /* we could check the real used format after creation, but for now: */ + if ((drm_mod_properties[i].drmFormatModifierTilingFeatures & VK_FORMAT_FEATURE_BLIT_SRC_BIT) == 0) + can_blit = FALSE; + + modifiers[n_modifiers++] = drm_mod_properties[i].drmFormatModifier; + } + + if (n_modifiers > 0) + break; + } + + if (n_modifiers > 0) + break; + + if (format == GDK_MEMORY_R8G8B8A8_PREMULTIPLIED) + return NULL; + + format = gsk_memory_format_get_fallback (format); + } + + self = g_object_new (GSK_TYPE_VULKAN_IMAGE, NULL); + + self->display = g_object_ref (gsk_gpu_device_get_display (GSK_GPU_DEVICE (device))); + gdk_display_ref_vulkan (self->display); + self->vk_tiling = VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT; + self->vk_format = format_info->format; + self->vk_pipeline_stage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; + self->vk_image_layout = VK_IMAGE_LAYOUT_UNDEFINED; + self->vk_access = 0; + + gsk_gpu_image_setup (GSK_GPU_IMAGE (self), + format_info->flags | GSK_GPU_IMAGE_EXTERNAL | + (gdk_memory_format_alpha (format) == GDK_MEMORY_ALPHA_STRAIGHT ? GSK_GPU_IMAGE_STRAIGHT_ALPHA : 0) | + (can_blit ? 0 : GSK_GPU_IMAGE_NO_BLIT), + format, + width, height); + + res = vkCreateImage (vk_device, + &(VkImageCreateInfo) { + .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, + .flags = 0, + .imageType = VK_IMAGE_TYPE_2D, + .format = format_info->format, + .extent = { width, height, 1 }, + .mipLevels = 1, + .arrayLayers = 1, + .samples = VK_SAMPLE_COUNT_1_BIT, + .tiling = VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT, + .usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | + (can_blit ? 0 : VK_IMAGE_USAGE_TRANSFER_SRC_BIT), + .sharingMode = VK_SHARING_MODE_EXCLUSIVE, + .initialLayout = self->vk_image_layout, + .pNext = &(VkExternalMemoryImageCreateInfo) { + .sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO, + .handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT, + .pNext = &(VkImageDrmFormatModifierListCreateInfoEXT) { + .sType = VK_STRUCTURE_TYPE_IMAGE_DRM_FORMAT_MODIFIER_LIST_CREATE_INFO_EXT, + .drmFormatModifierCount = n_modifiers, + .pDrmFormatModifiers = modifiers + } + }, + }, + NULL, + &self->vk_image); + if (res != VK_SUCCESS) + { + gsk_vulkan_handle_result (res, "vkCreateImage"); + return NULL; + } + + vkGetImageMemoryRequirements (vk_device, + self->vk_image, + &requirements); + + self->allocator = gsk_vulkan_device_get_external_allocator (device); + gsk_vulkan_allocator_ref (self->allocator); + + gsk_vulkan_alloc (self->allocator, + requirements.size, + requirements.alignment, + &self->allocation); + + GSK_VK_CHECK (vkAllocateMemory, vk_device, + &(VkMemoryAllocateInfo) { + .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, + .allocationSize = requirements.size, + .memoryTypeIndex = g_bit_nth_lsf (requirements.memoryTypeBits, -1), + .pNext = &(VkExportMemoryAllocateInfo) { + .sType = VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO, + .handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT, + } + }, + NULL, + &self->allocation.vk_memory); + + GSK_VK_CHECK (vkBindImageMemory, vk_device, + self->vk_image, + self->allocation.vk_memory, + self->allocation.offset); + + gsk_vulkan_image_create_view (self, VK_NULL_HANDLE, format_info); + + return GSK_GPU_IMAGE (self); +} + GskGpuImage * gsk_vulkan_image_new_for_dmabuf (GskVulkanDevice *device, GdkTexture *texture) @@ -1129,6 +1320,128 @@ gsk_vulkan_image_new_for_dmabuf (GskVulkanDevice *device, return GSK_GPU_IMAGE (self); } + +static void +close_the_fd (gpointer the_fd) +{ + close (GPOINTER_TO_INT (the_fd)); +} + +static guint +gsk_vulkan_image_get_n_planes (GskVulkanImage *self, + guint64 modifier) +{ + VkDrmFormatModifierPropertiesEXT drm_mod_properties[100]; + VkDrmFormatModifierPropertiesListEXT drm_properties = { + .sType = VK_STRUCTURE_TYPE_DRM_FORMAT_MODIFIER_PROPERTIES_LIST_EXT, + .drmFormatModifierCount = G_N_ELEMENTS (drm_mod_properties), + .pDrmFormatModifierProperties = drm_mod_properties, + }; + VkFormatProperties2 properties = { + .sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2, + .pNext = &drm_properties + }; + gsize i; + + vkGetPhysicalDeviceFormatProperties2 (self->display->vk_physical_device, + self->vk_format, + &properties); + + for (i = 0; i < drm_properties.drmFormatModifierCount; i++) + { + if (drm_mod_properties[i].drmFormatModifier == modifier) + return drm_mod_properties[i].drmFormatModifierPlaneCount; + } + + g_return_val_if_reached (0); +} + +GdkTexture * +gsk_vulkan_image_to_dmabuf_texture (GskVulkanImage *self) +{ + GskGpuImage *image = GSK_GPU_IMAGE (self); + GdkDmabufTextureBuilder *builder; + GError *error = NULL; + PFN_vkGetImageDrmFormatModifierPropertiesEXT func_vkGetImageDrmFormatModifierPropertiesEXT; + PFN_vkGetMemoryFdKHR func_vkGetMemoryFdKHR; + VkImageDrmFormatModifierPropertiesEXT properties = { + .sType = VK_STRUCTURE_TYPE_IMAGE_DRM_FORMAT_MODIFIER_PROPERTIES_EXT, + }; + VkSubresourceLayout layout; + GdkTexture *texture; + VkResult res; + guint32 fourcc; + int fd; + guint plane, n_planes; + + if (!(gsk_gpu_image_get_flags (image) & GSK_GPU_IMAGE_EXTERNAL)) + return FALSE; + + fourcc = gdk_memory_format_get_dmabuf_fourcc (gsk_gpu_image_get_format (image)); + if (fourcc == 0) + return FALSE; + + func_vkGetImageDrmFormatModifierPropertiesEXT = (PFN_vkGetImageDrmFormatModifierPropertiesEXT) + vkGetDeviceProcAddr (self->display->vk_device, "vkGetImageDrmFormatModifierPropertiesEXT"); + func_vkGetMemoryFdKHR = (PFN_vkGetMemoryFdKHR) vkGetDeviceProcAddr (self->display->vk_device, "vkGetMemoryFdKHR"); + res = GSK_VK_CHECK (func_vkGetImageDrmFormatModifierPropertiesEXT, self->display->vk_device, self->vk_image, &properties); + if (res != VK_SUCCESS) + return FALSE; + n_planes = gsk_vulkan_image_get_n_planes (self, properties.drmFormatModifier); + if (n_planes == 0 || n_planes > GDK_DMABUF_MAX_PLANES) + return FALSE; + res = GSK_VK_CHECK (func_vkGetMemoryFdKHR, self->display->vk_device, + &(VkMemoryGetFdInfoKHR) { + .sType = VK_STRUCTURE_TYPE_MEMORY_GET_FD_INFO_KHR, + .memory = self->allocation.vk_memory, + .handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT, + }, + &fd); + if (res != VK_SUCCESS) + return FALSE; + + builder = gdk_dmabuf_texture_builder_new (); + gdk_dmabuf_texture_builder_set_display (builder, self->display); + gdk_dmabuf_texture_builder_set_width (builder, gsk_gpu_image_get_width (image)); + gdk_dmabuf_texture_builder_set_height (builder, gsk_gpu_image_get_height (image)); + gdk_dmabuf_texture_builder_set_fourcc (builder, fourcc); + gdk_dmabuf_texture_builder_set_modifier (builder, properties.drmFormatModifier); + gdk_dmabuf_texture_builder_set_premultiplied (builder, !(gsk_gpu_image_get_flags (image) & GSK_GPU_IMAGE_STRAIGHT_ALPHA)); + gdk_dmabuf_texture_builder_set_n_planes (builder, n_planes); + + for (plane = 0; plane < n_planes; plane++) + { + static const VkImageAspectFlagBits aspect[GDK_DMABUF_MAX_PLANES] = { + VK_IMAGE_ASPECT_MEMORY_PLANE_0_BIT_EXT, + VK_IMAGE_ASPECT_MEMORY_PLANE_1_BIT_EXT, + VK_IMAGE_ASPECT_MEMORY_PLANE_2_BIT_EXT, + VK_IMAGE_ASPECT_MEMORY_PLANE_3_BIT_EXT + }; + vkGetImageSubresourceLayout (self->display->vk_device, + self->vk_image, + &(VkImageSubresource) { + .aspectMask = aspect[plane], + .mipLevel = 0, + .arrayLayer = 0 + }, + &layout); + gdk_dmabuf_texture_builder_set_fd (builder, plane, fd); + gdk_dmabuf_texture_builder_set_stride (builder, plane, layout.rowPitch); + gdk_dmabuf_texture_builder_set_offset (builder, plane, layout.offset); + } + + texture = gdk_dmabuf_texture_builder_build (builder, close_the_fd, GINT_TO_POINTER (fd), &error); + g_object_unref (builder); + if (texture == NULL) + { + GDK_DEBUG (VULKAN, "Failed to create dmabuf texture: %s", error->message); + g_clear_error (&error); + close (fd); + return NULL; + } + + return texture; +} #endif static void diff --git a/gsk/gpu/gskvulkanimageprivate.h b/gsk/gpu/gskvulkanimageprivate.h index 9019cf5ee2..899af834aa 100644 --- a/gsk/gpu/gskvulkanimageprivate.h +++ b/gsk/gpu/gskvulkanimageprivate.h @@ -31,8 +31,13 @@ GskGpuImage * gsk_vulkan_image_new_for_upload (GskVulk gsize width, gsize height); #ifdef HAVE_DMABUF +GskGpuImage * gsk_vulkan_image_new_dmabuf (GskVulkanDevice *device, + GdkMemoryFormat format, + gsize width, + gsize height); GskGpuImage * gsk_vulkan_image_new_for_dmabuf (GskVulkanDevice *device, GdkTexture *texture); +GdkTexture * gsk_vulkan_image_to_dmabuf_texture (GskVulkanImage *self); #endif guchar * gsk_vulkan_image_get_data (GskVulkanImage *self,