From e2518ea9dedf1ddb517f852ebdf45084633febcc Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sun, 30 Jan 2022 09:45:40 +0100 Subject: [PATCH] wip: add support for Vulkan software rendering VK_EXT_external_memory_host allows us to instruct llvmpipe to render to an existing chunk of memory. --- include/render/vulkan.h | 5 ++ render/vulkan/pixel_format.c | 6 ++ render/vulkan/renderer.c | 73 ++++++++++++++------ render/vulkan/texture.c | 127 +++++++++++++++++++++++++++++++++++ render/vulkan/vulkan.c | 28 ++++++-- render/wlr_renderer.c | 2 +- 6 files changed, 215 insertions(+), 26 deletions(-) diff --git a/include/render/vulkan.h b/include/render/vulkan.h index 1cba5a192..e80ebc105 100644 --- a/include/render/vulkan.h +++ b/include/render/vulkan.h @@ -58,12 +58,14 @@ struct wlr_vk_device { struct { PFN_vkGetMemoryFdPropertiesKHR getMemoryFdPropertiesKHR; + PFN_vkGetMemoryHostPointerPropertiesEXT getMemoryHostPointerPropertiesEXT; } api; uint32_t format_prop_count; struct wlr_vk_format_props *format_props; struct wlr_drm_format_set dmabuf_render_formats; struct wlr_drm_format_set dmabuf_texture_formats; + struct wlr_drm_format_set shm_render_formats; // supported formats for textures (contains only those formats // that support everything we need for textures) @@ -260,6 +262,9 @@ VkImage vulkan_import_dmabuf(struct wlr_vk_renderer *renderer, const struct wlr_dmabuf_attributes *attribs, VkDeviceMemory mems[static WLR_DMABUF_MAX_PLANES], uint32_t *n_mems, bool for_render); +VkImage vulkan_import_host_memory(struct wlr_vk_renderer *renderer, + void *host_ptr, uint32_t format, uint32_t width, uint32_t height, + uint32_t stride, VkDeviceMemory *mem, bool for_render); struct wlr_texture *vulkan_texture_from_buffer( struct wlr_renderer *wlr_renderer, struct wlr_buffer *buffer); void vulkan_texture_destroy(struct wlr_vk_texture *texture); diff --git a/render/vulkan/pixel_format.c b/render/vulkan/pixel_format.c index cfab5ffe8..eca111909 100644 --- a/render/vulkan/pixel_format.c +++ b/render/vulkan/pixel_format.c @@ -261,6 +261,12 @@ void vulkan_format_props_query(struct wlr_vk_device *dev, } // non-dmabuf texture properties + if (fmtp.formatProperties.optimalTilingFeatures & render_features) { + wlr_drm_format_set_add(&dev->shm_render_formats, + format->drm_format, DRM_FORMAT_MOD_INVALID); + add_fmt_props = true; + } + if (fmtp.formatProperties.optimalTilingFeatures & tex_features) { fmti.pNext = NULL; ifmtp.pNext = NULL; diff --git a/render/vulkan/renderer.c b/render/vulkan/renderer.c index 21b36bdd6..d17866c9b 100644 --- a/render/vulkan/renderer.c +++ b/render/vulkan/renderer.c @@ -425,26 +425,47 @@ static struct wlr_vk_render_buffer *create_render_buffer( buffer->wlr_buffer = wlr_buffer; buffer->renderer = renderer; + uint32_t format = DRM_FORMAT_INVALID; struct wlr_dmabuf_attributes dmabuf = {0}; - if (!wlr_buffer_get_dmabuf(wlr_buffer, &dmabuf)) { + void *data_ptr; + uint32_t data_ptr_format; + size_t data_ptr_stride; + if (wlr_buffer_get_dmabuf(wlr_buffer, &dmabuf)) { + format = dmabuf.format; + wlr_log(WLR_DEBUG, "vulkan create_render_buffer: DMA-BUF %.4s, %dx%d", + (const char *) &format, dmabuf.width, dmabuf.height); + + buffer->image = vulkan_import_dmabuf(renderer, &dmabuf, + buffer->memories, &buffer->mem_count, true); + } else if (wlr_buffer_begin_data_ptr_access(wlr_buffer, + WLR_BUFFER_DATA_PTR_ACCESS_READ, &data_ptr, &data_ptr_format, + &data_ptr_stride)) { + format = data_ptr_format; + wlr_log(WLR_DEBUG, "vulkan create_render_buffer: data ptr %.4s, %dx%d", + (const char *) &format, wlr_buffer->width, wlr_buffer->height); + + buffer->image = vulkan_import_host_memory(renderer, data_ptr, + data_ptr_format, wlr_buffer->width, wlr_buffer->height, + data_ptr_stride, &buffer->memories[0], true); + if (buffer->image) { + buffer->mem_count = 1; + } + + wlr_buffer_end_data_ptr_access(wlr_buffer); + } else { + wlr_log(WLR_ERROR, "Unsupported render buffer"); goto error_buffer; } - - wlr_log(WLR_DEBUG, "vulkan create_render_buffer: %.4s, %dx%d", - (const char*) &dmabuf.format, dmabuf.width, dmabuf.height); - - buffer->image = vulkan_import_dmabuf(renderer, &dmabuf, - buffer->memories, &buffer->mem_count, true); if (!buffer->image) { goto error_buffer; } VkDevice dev = renderer->dev->dev; const struct wlr_vk_format_props *fmt = vulkan_format_props_from_drm( - renderer->dev, dmabuf.format); + renderer->dev, format); if (fmt == NULL) { wlr_log(WLR_ERROR, "Unsupported pixel format %"PRIx32 " (%.4s)", - dmabuf.format, (const char*) &dmabuf.format); + format, (const char *) &format); goto error_buffer; } @@ -478,8 +499,8 @@ static struct wlr_vk_render_buffer *create_render_buffer( fb_info.attachmentCount = 1u; fb_info.pAttachments = &buffer->image_view; fb_info.flags = 0u; - fb_info.width = dmabuf.width; - fb_info.height = dmabuf.height; + fb_info.width = wlr_buffer->width; + fb_info.height = wlr_buffer->height; fb_info.layers = 1u; fb_info.renderPass = buffer->render_setup->render_pass; @@ -503,7 +524,6 @@ error_view: vkFreeMemory(dev, buffer->memories[i], NULL); } error_buffer: - wlr_dmabuf_attributes_finish(&dmabuf); free(buffer); return NULL; } @@ -892,7 +912,11 @@ static const struct wlr_drm_format_set *vulkan_get_dmabuf_texture_formats( static const struct wlr_drm_format_set *vulkan_get_render_formats( struct wlr_renderer *wlr_renderer) { struct wlr_vk_renderer *renderer = vulkan_get_renderer(wlr_renderer); - return &renderer->dev->dmabuf_render_formats; + if (renderer->dev->drm_fd >= 0) { + return &renderer->dev->dmabuf_render_formats; + } else { + return &renderer->dev->shm_render_formats; + } } static uint32_t vulkan_preferred_read_format( @@ -972,7 +996,12 @@ static int vulkan_get_drm_fd(struct wlr_renderer *wlr_renderer) { } static uint32_t vulkan_get_render_buffer_caps(struct wlr_renderer *wlr_renderer) { - return WLR_BUFFER_CAP_DMABUF; + struct wlr_vk_renderer *renderer = vulkan_get_renderer(wlr_renderer); + if (renderer->dev->drm_fd >= 0) { + return WLR_BUFFER_CAP_DMABUF; + } else { + return WLR_BUFFER_CAP_DATA_PTR; + } } static const struct wlr_renderer_impl renderer_impl = { @@ -1503,12 +1532,16 @@ struct wlr_renderer *wlr_vk_renderer_create_with_drm_fd(int drm_fd) { } // We duplicate it so it's not closed while we still need it. - dev->drm_fd = fcntl(drm_fd, F_DUPFD_CLOEXEC, 0); - if (dev->drm_fd < 0) { - wlr_log_errno(WLR_ERROR, "fcntl(F_DUPFD_CLOEXEC) failed"); - vulkan_device_destroy(dev); - vulkan_instance_destroy(ini); - return NULL; + if (drm_fd >= 0) { + dev->drm_fd = fcntl(drm_fd, F_DUPFD_CLOEXEC, 0); + if (dev->drm_fd < 0) { + wlr_log_errno(WLR_ERROR, "fcntl(F_DUPFD_CLOEXEC) failed"); + vulkan_device_destroy(dev); + vulkan_instance_destroy(ini); + return NULL; + } + } else { + dev->drm_fd = -1; } return vulkan_renderer_create_for_device(dev); diff --git a/render/vulkan/texture.c b/render/vulkan/texture.c index b705603cf..85201dfa3 100644 --- a/render/vulkan/texture.c +++ b/render/vulkan/texture.c @@ -577,6 +577,133 @@ error_image: return VK_NULL_HANDLE; } +VkImage vulkan_import_host_memory(struct wlr_vk_renderer *renderer, + void *host_ptr, uint32_t format, uint32_t width, uint32_t height, + uint32_t stride, VkDeviceMemory *mem, bool for_render) { + VkResult res; + VkDevice dev = renderer->dev->dev; + + *mem = VK_NULL_HANDLE; + + struct wlr_vk_format_props *fmt = + vulkan_format_props_from_drm(renderer->dev, format); + if (fmt == NULL) { + wlr_log(WLR_ERROR, "Unsupported pixel format %"PRIx32 " (%.4s)", + format, (const char*) &format); + return VK_NULL_HANDLE; + } + + VkExternalMemoryHandleTypeFlagBits htype = + VK_EXTERNAL_MEMORY_HANDLE_TYPE_HOST_ALLOCATION_BIT_EXT; + + VkImageCreateInfo img_info = {0}; + img_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; + img_info.imageType = VK_IMAGE_TYPE_2D; + img_info.format = fmt->format.vk_format; + img_info.mipLevels = 1; + img_info.arrayLayers = 1; + img_info.samples = VK_SAMPLE_COUNT_1_BIT; + img_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + img_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + img_info.extent = (VkExtent3D) { width, height, 1 }; + img_info.usage = for_render ? + VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT : + VK_IMAGE_USAGE_SAMPLED_BIT; + + VkExternalMemoryImageCreateInfo eimg = {0}; + eimg.sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO; + eimg.handleTypes = htype; + img_info.pNext = &eimg; + + VkImage image; + res = vkCreateImage(dev, &img_info, NULL, &image); + if (res != VK_SUCCESS) { + wlr_vk_error("vkCreateImage", res); + return VK_NULL_HANDLE; + } + + VkMemoryHostPointerPropertiesEXT host_ptr_props = {0}; + host_ptr_props.sType = VK_STRUCTURE_TYPE_MEMORY_HOST_POINTER_PROPERTIES_EXT; + res = renderer->dev->api.getMemoryHostPointerPropertiesEXT(dev, htype, + host_ptr, &host_ptr_props); + if (res != VK_SUCCESS) { + wlr_vk_error("getMemoryHostPointerPropertiesEXT", res); + goto error_image; + } + + VkImageMemoryRequirementsInfo2 memri = {0}; + memri.image = image; + memri.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2; + + VkMemoryRequirements2 memr = {0}; + memr.sType = VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2; + + vkGetImageMemoryRequirements2(dev, &memri, &memr); + + int mem_index = vulkan_find_mem_type(renderer->dev, 0, + memr.memoryRequirements.memoryTypeBits & host_ptr_props.memoryTypeBits); + if (mem_index < 0) { + wlr_log(WLR_ERROR, "no valid memory type index"); + goto error_image; + } + + if ((uintptr_t)host_ptr % memr.memoryRequirements.alignment != 0) { + wlr_log(WLR_ERROR, "Invalid memory alignment " + "(%p not aligned to %zu bytes)", host_ptr, + (size_t)memr.memoryRequirements.alignment); + goto error_image; + } + + if (height * stride < memr.memoryRequirements.size) { + wlr_log(WLR_ERROR, "Invalid memory size (%zu bytes required, " + "but has %zu bytes)", (size_t)memr.memoryRequirements.size, + (size_t)height * stride); + goto error_image; + } + + VkMemoryAllocateInfo memi = {0}; + memi.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + memi.allocationSize = memr.memoryRequirements.size; + memi.memoryTypeIndex = mem_index; + + VkImportMemoryHostPointerInfoEXT importi = {0}; + importi.sType = VK_STRUCTURE_TYPE_IMPORT_MEMORY_HOST_POINTER_INFO_EXT; + importi.handleType = htype; + importi.pHostPointer = host_ptr; + memi.pNext = &importi; + + VkMemoryDedicatedAllocateInfo dedi = {0}; + dedi.sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO; + dedi.image = image; + importi.pNext = &dedi; + + res = vkAllocateMemory(dev, &memi, NULL, mem); + if (res != VK_SUCCESS) { + wlr_vk_error("vkAllocateMemory failed", res); + goto error_image; + } + + VkBindImageMemoryInfo bindi = {0}; + bindi.image = image; + bindi.memory = *mem; + bindi.memoryOffset = 0; + bindi.sType = VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_INFO; + + res = vkBindImageMemory2(dev, 1, &bindi); + if (res != VK_SUCCESS) { + wlr_vk_error("vkBindMemory failed", res); + goto error_image; + } + + return image; + +error_image: + vkFreeMemory(dev, *mem, NULL); + vkDestroyImage(dev, image, NULL); + + return VK_NULL_HANDLE; +} + static struct wlr_texture *vulkan_texture_from_dmabuf(struct wlr_renderer *wlr_renderer, struct wlr_dmabuf_attributes *attribs) { struct wlr_vk_renderer *renderer = vulkan_get_renderer(wlr_renderer); diff --git a/render/vulkan/vulkan.c b/render/vulkan/vulkan.c index 7e9e93962..5bada068f 100644 --- a/render/vulkan/vulkan.c +++ b/render/vulkan/vulkan.c @@ -303,7 +303,7 @@ VkPhysicalDevice vulkan_find_drm_phdev(struct wlr_vk_instance *ini, int drm_fd) } struct stat drm_stat = {0}; - if (fstat(drm_fd, &drm_stat) != 0) { + if (drm_fd >= 0 && fstat(drm_fd, &drm_stat) != 0) { wlr_log_errno(WLR_ERROR, "fstat failed"); return VK_NULL_HANDLE; } @@ -370,6 +370,15 @@ VkPhysicalDevice vulkan_find_drm_phdev(struct wlr_vk_instance *ini, int drm_fd) wlr_log(WLR_INFO, " Driver name: %s (%s)", driver_props.driverName, driver_props.driverInfo); } + if (drm_fd < 0) { + if (phdev_props.deviceType == VK_PHYSICAL_DEVICE_TYPE_CPU) { + wlr_log(WLR_INFO, "Found matching Vulkan physical device: %s", + phdev_props.deviceName); + return phdev; + } + continue; + } + if (!has_drm_props) { wlr_log(WLR_DEBUG, " Ignoring physical device \"%s\": " "VK_EXT_physical_device_drm not supported", @@ -449,9 +458,9 @@ struct wlr_vk_device *vulkan_device_create(struct wlr_vk_instance *ini, const char *names[] = { VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME, VK_KHR_IMAGE_FORMAT_LIST_EXTENSION_NAME, // or vulkan 1.2 - VK_EXT_EXTERNAL_MEMORY_DMA_BUF_EXTENSION_NAME, - VK_EXT_QUEUE_FAMILY_FOREIGN_EXTENSION_NAME, - VK_EXT_IMAGE_DRM_FORMAT_MODIFIER_EXTENSION_NAME, + //VK_EXT_EXTERNAL_MEMORY_DMA_BUF_EXTENSION_NAME, + //VK_EXT_QUEUE_FAMILY_FOREIGN_EXTENSION_NAME, + //VK_EXT_IMAGE_DRM_FORMAT_MODIFIER_EXTENSION_NAME, }; unsigned nc = sizeof(names) / sizeof(names[0]); @@ -466,6 +475,11 @@ struct wlr_vk_device *vulkan_device_create(struct wlr_vk_instance *ini, dev->extensions[dev->extension_count++] = names[i]; } + const char *name = "VK_EXT_external_memory_host"; + if (find_extensions(avail_ext_props, avail_extc, &name, 1) == NULL) { + dev->extensions[dev->extension_count++] = name; + } + // queue families { uint32_t qfam_count; @@ -514,12 +528,15 @@ struct wlr_vk_device *vulkan_device_create(struct wlr_vk_instance *ini, // load api dev->api.getMemoryFdPropertiesKHR = (PFN_vkGetMemoryFdPropertiesKHR) vkGetDeviceProcAddr(dev->dev, "vkGetMemoryFdPropertiesKHR"); - if (!dev->api.getMemoryFdPropertiesKHR) { wlr_log(WLR_ERROR, "Failed to retrieve required dev function pointers"); goto error; } + dev->api.getMemoryHostPointerPropertiesEXT = + (PFN_vkGetMemoryHostPointerPropertiesEXT) + vkGetDeviceProcAddr(dev->dev, "vkGetMemoryHostPointerPropertiesEXT"); + // - check device format support - size_t max_fmts; const struct wlr_vk_format *fmts = vulkan_get_format_list(&max_fmts); @@ -556,6 +573,7 @@ void vulkan_device_destroy(struct wlr_vk_device *dev) { wlr_drm_format_set_finish(&dev->dmabuf_render_formats); wlr_drm_format_set_finish(&dev->dmabuf_texture_formats); + wlr_drm_format_set_finish(&dev->shm_render_formats); for (unsigned i = 0u; i < dev->format_prop_count; ++i) { vulkan_format_props_finish(&dev->format_props[i]); diff --git a/render/wlr_renderer.c b/render/wlr_renderer.c index 2b5cf9945..e3dbc1fe9 100644 --- a/render/wlr_renderer.c +++ b/render/wlr_renderer.c @@ -283,7 +283,7 @@ struct wlr_renderer *renderer_autocreate_with_drm_fd(int drm_fd) { #endif #if WLR_HAS_VULKAN_RENDERER if (strcmp(name, "vulkan") == 0) { - return wlr_vk_renderer_create_with_drm_fd(drm_fd); + return wlr_vk_renderer_create_with_drm_fd(-1); } #endif if (strcmp(name, "pixman") == 0) {