From 35b080ec610700357fc06905cc9be17dd5a2fb0a Mon Sep 17 00:00:00 2001 From: columbarius Date: Thu, 10 Aug 2023 04:03:08 +0200 Subject: [PATCH] vulkan: split spa_vulkan_use_buffers into separate allocation and import functions This commit add helpers to create and to import a DmaBuf via the external_dmabuf_info struct. The import path is based on the image creation of wlroots and the create path is inspired by the wlroots vulkan allocator MR. --- spa/plugins/vulkan/vulkan-compute-utils.c | 151 ++++---------- spa/plugins/vulkan/vulkan-utils.c | 243 ++++++++++++++++++++++ spa/plugins/vulkan/vulkan-utils.h | 13 ++ 3 files changed, 301 insertions(+), 106 deletions(-) diff --git a/spa/plugins/vulkan/vulkan-compute-utils.c b/spa/plugins/vulkan/vulkan-compute-utils.c index c91987da5..0796b697d 100644 --- a/spa/plugins/vulkan/vulkan-compute-utils.c +++ b/spa/plugins/vulkan/vulkan-compute-utils.c @@ -31,12 +31,6 @@ #define VULKAN_INSTANCE_FUNCTION(name) \ PFN_##name name = (PFN_##name)vkGetInstanceProcAddr(s->base.instance, #name) -static uint32_t findMemoryType(struct vulkan_compute_state *s, - uint32_t memoryTypeBits, VkMemoryPropertyFlags properties) -{ - return vulkan_memoryType_find(&s->base, memoryTypeBits, properties); -} - static int createFence(struct vulkan_compute_state *s) { VkFenceCreateInfo createInfo = { .sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, @@ -334,115 +328,60 @@ static void clear_streams(struct vulkan_compute_state *s) int spa_vulkan_use_buffers(struct vulkan_compute_state *s, struct vulkan_stream *p, uint32_t flags, struct spa_video_info_dsp *dsp_info, uint32_t n_buffers, struct spa_buffer **buffers) { - uint32_t i; - VULKAN_INSTANCE_FUNCTION(vkGetMemoryFdKHR); - VkFormat format = vulkan_id_to_vkformat(dsp_info->format); if (format == VK_FORMAT_UNDEFINED) return -1; + vulkan_wait_idle(&s->base); clear_buffers(s, p); - for (i = 0; i < n_buffers; i++) { - VkExternalMemoryImageCreateInfo extInfo; - VkImageCreateInfo imageCreateInfo = { - .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, - .imageType = VK_IMAGE_TYPE_2D, - .format = format, - .extent.width = s->constants.width, - .extent.height = s->constants.height, - .extent.depth = 1, - .mipLevels = 1, - .arrayLayers = 1, - .samples = VK_SAMPLE_COUNT_1_BIT, - .tiling = VK_IMAGE_TILING_LINEAR, - .usage = p->direction == SPA_DIRECTION_OUTPUT ? - VK_IMAGE_USAGE_STORAGE_BIT: - VK_IMAGE_USAGE_SAMPLED_BIT, - .sharingMode = VK_SHARING_MODE_EXCLUSIVE, - .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, - }; - - if (!(flags & SPA_NODE_BUFFERS_FLAG_ALLOC)) { - extInfo = (VkExternalMemoryImageCreateInfo) { - .sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO, - .handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT, - }; - imageCreateInfo.pNext = &extInfo; - } - - VK_CHECK_RESULT(vkCreateImage(s->base.device, - &imageCreateInfo, NULL, &p->buffers[i].image)); - - VkMemoryRequirements memoryRequirements; - vkGetImageMemoryRequirements(s->base.device, - p->buffers[i].image, &memoryRequirements); - - VkMemoryAllocateInfo allocateInfo = { - .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, - .allocationSize = memoryRequirements.size, - .memoryTypeIndex = findMemoryType(s, - memoryRequirements.memoryTypeBits, - VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | - VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT), - }; - - if (flags & SPA_NODE_BUFFERS_FLAG_ALLOC) { - VK_CHECK_RESULT(vkAllocateMemory(s->base.device, - &allocateInfo, NULL, &p->buffers[i].memory)); - - const VkMemoryGetFdInfoKHR getFdInfo = { - .sType = VK_STRUCTURE_TYPE_MEMORY_GET_FD_INFO_KHR, - .memory = p->buffers[i].memory, - .handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT - }; - int fd; - - VK_CHECK_RESULT(vkGetMemoryFdKHR(s->base.device, &getFdInfo, &fd)); - - spa_log_info(s->log, "export DMABUF %zd", memoryRequirements.size); - -// buffers[i]->datas[0].type = SPA_DATA_DmaBuf; - buffers[i]->datas[0].type = SPA_DATA_MemFd; - buffers[i]->datas[0].fd = fd; - buffers[i]->datas[0].flags = SPA_DATA_FLAG_READABLE; - buffers[i]->datas[0].mapoffset = 0; - buffers[i]->datas[0].maxsize = memoryRequirements.size; - p->buffers[i].fd = fd; + bool alloc = flags & SPA_NODE_BUFFERS_FLAG_ALLOC; + int ret; + p->n_buffers = 0; + for (uint32_t i = 0; i < n_buffers; i++) { + if (alloc) { + if (SPA_FLAG_IS_SET(buffers[i]->datas[0].type, 1<modifier, + .size.width = s->constants.width, + .size.height = s->constants.height, + .usage = p->direction == SPA_DIRECTION_OUTPUT + ? VK_IMAGE_USAGE_STORAGE_BIT + : VK_IMAGE_USAGE_SAMPLED_BIT, + .spa_buf = buffers[i], + }; + ret = vulkan_create_dmabuf(&s->base, &dmabuf_info, &p->buffers[i]); + } else { + spa_log_error(s->log, "Unsupported buffer type mask %d", buffers[i]->datas[0].type); + return -1; + } } else { - VkImportMemoryFdInfoKHR importInfo = { - .sType = VK_STRUCTURE_TYPE_IMPORT_MEMORY_FD_INFO_KHR, - .handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT, - .fd = fcntl(buffers[i]->datas[0].fd, F_DUPFD_CLOEXEC, 0), - }; - allocateInfo.pNext = &importInfo; - p->buffers[i].fd = -1; - spa_log_info(s->log, "import DMABUF"); - - VK_CHECK_RESULT(vkAllocateMemory(s->base.device, - &allocateInfo, NULL, &p->buffers[i].memory)); + switch (buffers[i]->datas[0].type) { + case SPA_DATA_DmaBuf:; + struct external_dmabuf_info dmabuf_info = { + .format = format, + .modifier = dsp_info->modifier, + .size.width = s->constants.width, + .size.height = s->constants.height, + .usage = p->direction == SPA_DIRECTION_OUTPUT + ? VK_IMAGE_USAGE_STORAGE_BIT + : VK_IMAGE_USAGE_SAMPLED_BIT, + .spa_buf = buffers[i], + }; + ret = vulkan_import_dmabuf(&s->base, &dmabuf_info, &p->buffers[i]); + break; + default: + spa_log_error(s->log, "Unsupported buffer type %d", buffers[i]->datas[0].type); + return -1; + } } - VK_CHECK_RESULT(vkBindImageMemory(s->base.device, - p->buffers[i].image, p->buffers[i].memory, 0)); - - VkImageViewCreateInfo viewInfo = { - .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, - .image = p->buffers[i].image, - .viewType = VK_IMAGE_VIEW_TYPE_2D, - .format = format, - .components.r = VK_COMPONENT_SWIZZLE_R, - .components.g = VK_COMPONENT_SWIZZLE_G, - .components.b = VK_COMPONENT_SWIZZLE_B, - .components.a = VK_COMPONENT_SWIZZLE_A, - .subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - .subresourceRange.levelCount = 1, - .subresourceRange.layerCount = 1, - }; - - VK_CHECK_RESULT(vkCreateImageView(s->base.device, - &viewInfo, NULL, &p->buffers[i].view)); + if (ret != 0) { + spa_log_error(s->log, "Failed to use buffer %d", i); + return ret; + } + p->n_buffers++; } - p->n_buffers = n_buffers; return 0; } diff --git a/spa/plugins/vulkan/vulkan-utils.c b/spa/plugins/vulkan/vulkan-utils.c index de88a7053..fea1b9b39 100644 --- a/spa/plugins/vulkan/vulkan-utils.c +++ b/spa/plugins/vulkan/vulkan-utils.c @@ -29,6 +29,9 @@ //#define ENABLE_VALIDATION +#define VULKAN_INSTANCE_FUNCTION(name) \ + PFN_##name name = (PFN_##name)vkGetInstanceProcAddr(s->instance, #name) + static int vkresult_to_errno(VkResult result) { switch (result) { @@ -401,6 +404,246 @@ void vulkan_buffer_clear(struct vulkan_base *s, struct vulkan_buffer *buffer) vkDestroyImageView(s->device, buffer->view, NULL); } +static VkImageAspectFlagBits mem_plane_aspect(uint32_t i) +{ + switch (i) { + case 0: return VK_IMAGE_ASPECT_MEMORY_PLANE_0_BIT_EXT; + case 1: return VK_IMAGE_ASPECT_MEMORY_PLANE_1_BIT_EXT; + case 2: return VK_IMAGE_ASPECT_MEMORY_PLANE_2_BIT_EXT; + case 3: return VK_IMAGE_ASPECT_MEMORY_PLANE_3_BIT_EXT; + default: abort(); // unreachable + } +} + +static int allocate_dmabuf(struct vulkan_base *s, VkFormat format, uint32_t modifierCount, uint64_t *modifiers, VkImageUsageFlags usage, struct spa_rectangle *size, struct vulkan_buffer *vk_buf) +{ + VkImageDrmFormatModifierListCreateInfoEXT imageDrmFormatModifierListCreateInfo = { + .sType = VK_STRUCTURE_TYPE_IMAGE_DRM_FORMAT_MODIFIER_LIST_CREATE_INFO_EXT, + .drmFormatModifierCount = modifierCount, + .pDrmFormatModifiers = modifiers, + }; + + VkExternalMemoryImageCreateInfo extMemoryImageCreateInfo = { + .sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO, + .handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT, + }; + extMemoryImageCreateInfo.pNext = &imageDrmFormatModifierListCreateInfo; + + VkImageCreateInfo imageCreateInfo = { + .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, + .imageType = VK_IMAGE_TYPE_2D, + .format = format, + .extent.width = size->width, + .extent.height = size->height, + .extent.depth = 1, + .mipLevels = 1, + .arrayLayers = 1, + .samples = VK_SAMPLE_COUNT_1_BIT, + .tiling = VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT, + .usage = usage, + .sharingMode = VK_SHARING_MODE_EXCLUSIVE, + .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, + }; + imageCreateInfo.pNext = &extMemoryImageCreateInfo; + + VK_CHECK_RESULT(vkCreateImage(s->device, + &imageCreateInfo, NULL, &vk_buf->image)); + + VkMemoryRequirements memoryRequirements = {0}; + vkGetImageMemoryRequirements(s->device, + vk_buf->image, &memoryRequirements); + + VkExportMemoryAllocateInfo exportAllocateInfo = { + .sType = VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO, + .handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT, + }; + VkMemoryAllocateInfo allocateInfo = { + .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, + .allocationSize = memoryRequirements.size, + .memoryTypeIndex = vulkan_memoryType_find(s, + memoryRequirements.memoryTypeBits, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT), + }; + allocateInfo.pNext = &exportAllocateInfo; + + VK_CHECK_RESULT(vkAllocateMemory(s->device, + &allocateInfo, NULL, &vk_buf->memory)); + + VK_CHECK_RESULT(vkBindImageMemory(s->device, + vk_buf->image, vk_buf->memory, 0)); + + return 0; +} + +int vulkan_create_dmabuf(struct vulkan_base *s, struct external_dmabuf_info *info, struct vulkan_buffer *vk_buf) +{ + VULKAN_INSTANCE_FUNCTION(vkGetMemoryFdKHR); + + if (info->spa_buf->n_datas != 1) + return -1; + + VK_CHECK_RESULT(allocate_dmabuf(s, info->format, 1, &info->modifier, info->usage, &info->size, vk_buf)); + + const VkMemoryGetFdInfoKHR getFdInfo = { + .sType = VK_STRUCTURE_TYPE_MEMORY_GET_FD_INFO_KHR, + .memory = vk_buf->memory, + .handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT + }; + int fd = -1; + VK_CHECK_RESULT(vkGetMemoryFdKHR(s->device, &getFdInfo, &fd)); + + const struct vulkan_modifier_info *modInfo = vulkan_modifierInfo_find(s, info->format, info->modifier); + + if (info->spa_buf->n_datas != modInfo->props.drmFormatModifierPlaneCount) + return -1; + + VkMemoryRequirements memoryRequirements = {0}; + vkGetImageMemoryRequirements(s->device, + vk_buf->image, &memoryRequirements); + + spa_log_info(s->log, "export DMABUF %zd", memoryRequirements.size); + + for (uint32_t i = 0; i < info->spa_buf->n_datas; i++) { + VkImageSubresource subresource = { + .aspectMask = mem_plane_aspect(i), + }; + VkSubresourceLayout subresLayout = {0}; + vkGetImageSubresourceLayout(s->device, vk_buf->image, &subresource, &subresLayout); + + info->spa_buf->datas[i].type = SPA_DATA_DmaBuf; + info->spa_buf->datas[i].fd = fd; + info->spa_buf->datas[i].flags = SPA_DATA_FLAG_READABLE; + info->spa_buf->datas[i].mapoffset = 0; + info->spa_buf->datas[i].chunk->offset = subresLayout.offset; + info->spa_buf->datas[i].chunk->stride = subresLayout.rowPitch; + info->spa_buf->datas[i].chunk->size = subresLayout.size; + info->spa_buf->datas[i].maxsize = memoryRequirements.size; + } + vk_buf->fd = fd; + + VkImageViewCreateInfo viewInfo = { + .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, + .image = vk_buf->image, + .viewType = VK_IMAGE_VIEW_TYPE_2D, + .format = info->format, + .components.r = VK_COMPONENT_SWIZZLE_R, + .components.g = VK_COMPONENT_SWIZZLE_G, + .components.b = VK_COMPONENT_SWIZZLE_B, + .components.a = VK_COMPONENT_SWIZZLE_A, + .subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .subresourceRange.levelCount = 1, + .subresourceRange.layerCount = 1, + }; + + VK_CHECK_RESULT(vkCreateImageView(s->device, + &viewInfo, NULL, &vk_buf->view)); + return 0; +} + +int vulkan_import_dmabuf(struct vulkan_base *s, struct external_dmabuf_info *info, struct vulkan_buffer *vk_buf) +{ + + if (info->spa_buf->n_datas == 0 || info->spa_buf->n_datas > DMABUF_MAX_PLANES) + return -1; + + struct vulkan_modifier_info *modProps = vulkan_modifierInfo_find(s, info->format, info->modifier); + if (!modProps) + return -1; + + uint32_t planeCount = info->spa_buf->n_datas; + + if (planeCount != modProps->props.drmFormatModifierPlaneCount) + return -1; + + if (info->size.width > modProps->max_extent.width || info->size.height > modProps->max_extent.height) + return -1; + + VkSubresourceLayout planeLayouts[DMABUF_MAX_PLANES] = {0}; + for (uint32_t i = 0; i < planeCount; i++) { + planeLayouts[i].offset = info->spa_buf->datas[i].chunk->offset; + planeLayouts[i].rowPitch = info->spa_buf->datas[i].chunk->stride; + planeLayouts[i].size = 0; + } + + VkImageDrmFormatModifierExplicitCreateInfoEXT modInfo = { + .sType = VK_STRUCTURE_TYPE_IMAGE_DRM_FORMAT_MODIFIER_EXPLICIT_CREATE_INFO_EXT, + .drmFormatModifierPlaneCount = planeCount, + .drmFormatModifier = info->modifier, + .pPlaneLayouts = planeLayouts, + }; + + VkExternalMemoryImageCreateInfo extInfo = { + .sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO, + .handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT, + .pNext = &modInfo, + }; + + VkImageCreateInfo imageCreateInfo = { + .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, + .imageType = VK_IMAGE_TYPE_2D, + .format = info->format, + .extent.width = info->size.width, + .extent.height = info->size.height, + .extent.depth = 1, + .mipLevels = 1, + .arrayLayers = 1, + .samples = VK_SAMPLE_COUNT_1_BIT, + .tiling = VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT, + .usage = info->usage, + .sharingMode = VK_SHARING_MODE_EXCLUSIVE, + .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, + .pNext = &extInfo, + }; + + VK_CHECK_RESULT(vkCreateImage(s->device, + &imageCreateInfo, NULL, &vk_buf->image)); + + VkMemoryRequirements memoryRequirements; + vkGetImageMemoryRequirements(s->device, + vk_buf->image, &memoryRequirements); + + vk_buf->fd = fcntl(info->spa_buf->datas[0].fd, F_DUPFD_CLOEXEC, 0); + VkImportMemoryFdInfoKHR importInfo = { + .sType = VK_STRUCTURE_TYPE_IMPORT_MEMORY_FD_INFO_KHR, + .handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT, + .fd = fcntl(info->spa_buf->datas[0].fd, F_DUPFD_CLOEXEC, 0), + }; + + VkMemoryAllocateInfo allocateInfo = { + .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, + .allocationSize = memoryRequirements.size, + .memoryTypeIndex = vulkan_memoryType_find(s, + memoryRequirements.memoryTypeBits, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT), + }; + allocateInfo.pNext = &importInfo; + + spa_log_info(s->log, "import DMABUF"); + + VK_CHECK_RESULT(vkAllocateMemory(s->device, + &allocateInfo, NULL, &vk_buf->memory)); + VK_CHECK_RESULT(vkBindImageMemory(s->device, + vk_buf->image, vk_buf->memory, 0)); + + VkImageViewCreateInfo viewInfo = { + .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, + .image = vk_buf->image, + .viewType = VK_IMAGE_VIEW_TYPE_2D, + .format = info->format, + .components.r = VK_COMPONENT_SWIZZLE_R, + .components.g = VK_COMPONENT_SWIZZLE_G, + .components.b = VK_COMPONENT_SWIZZLE_B, + .components.a = VK_COMPONENT_SWIZZLE_A, + .subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .subresourceRange.levelCount = 1, + .subresourceRange.layerCount = 1, + }; + + VK_CHECK_RESULT(vkCreateImageView(s->device, + &viewInfo, NULL, &vk_buf->view)); + return 0; +} + int vulkan_stream_init(struct vulkan_stream *stream, enum spa_direction direction, struct spa_dict *props) { diff --git a/spa/plugins/vulkan/vulkan-utils.h b/spa/plugins/vulkan/vulkan-utils.h index 69a8fe44b..25457d552 100644 --- a/spa/plugins/vulkan/vulkan-utils.h +++ b/spa/plugins/vulkan/vulkan-utils.h @@ -2,6 +2,8 @@ /* SPDX-FileCopyrightText: Copyright © 2019 Wim Taymans */ /* SPDX-License-Identifier: MIT */ +#pragma once + #include #include @@ -34,6 +36,17 @@ return _res; \ } +struct external_dmabuf_info { + VkFormat format; + uint64_t modifier; + struct spa_rectangle size; + VkImageUsageFlags usage; + struct spa_buffer *spa_buf; +}; + +int vulkan_create_dmabuf(struct vulkan_base *s, struct external_dmabuf_info *info, struct vulkan_buffer *vk_buf); +int vulkan_import_dmabuf(struct vulkan_base *s, struct external_dmabuf_info *info, struct vulkan_buffer *vk_buf); + int vulkan_commandPool_create(struct vulkan_base *s, VkCommandPool *commandPool); int vulkan_commandBuffer_create(struct vulkan_base *s, VkCommandPool commandPool, VkCommandBuffer *commandBuffer);