diff --git a/src/vulkan/wsi/wsi_common.c b/src/vulkan/wsi/wsi_common.c index b90c8e420e2..7094171db7d 100644 --- a/src/vulkan/wsi/wsi_common.c +++ b/src/vulkan/wsi/wsi_common.c @@ -64,6 +64,7 @@ wsi_device_init(struct wsi_device *wsi, wsi->sw = sw_device; #define WSI_GET_CB(func) \ PFN_vk##func func = (PFN_vk##func)proc_addr(pdevice, "vk" #func) + WSI_GET_CB(GetPhysicalDeviceExternalSemaphoreProperties); WSI_GET_CB(GetPhysicalDeviceProperties2); WSI_GET_CB(GetPhysicalDeviceMemoryProperties); WSI_GET_CB(GetPhysicalDeviceQueueFamilyProperties); @@ -83,6 +84,23 @@ wsi_device_init(struct wsi_device *wsi, GetPhysicalDeviceMemoryProperties(pdevice, &wsi->memory_props); GetPhysicalDeviceQueueFamilyProperties(pdevice, &wsi->queue_family_count, NULL); + for (VkExternalSemaphoreHandleTypeFlags handle_type = 1; + handle_type <= VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT; + handle_type <<= 1) { + const VkPhysicalDeviceExternalSemaphoreInfo esi = { + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_SEMAPHORE_INFO, + .handleType = handle_type, + }; + VkExternalSemaphoreProperties esp = { + .sType = VK_STRUCTURE_TYPE_EXTERNAL_SEMAPHORE_PROPERTIES, + }; + GetPhysicalDeviceExternalSemaphoreProperties(pdevice, &esi, &esp); + + if (esp.externalSemaphoreFeatures & + VK_EXTERNAL_SEMAPHORE_FEATURE_EXPORTABLE_BIT) + wsi->semaphore_export_handle_types |= handle_type; + } + list_inithead(&wsi->hotplug_fences); #define WSI_GET_CB(func) \ @@ -116,6 +134,7 @@ wsi_device_init(struct wsi_device *wsi, WSI_GET_CB(GetPhysicalDeviceFormatProperties); WSI_GET_CB(GetPhysicalDeviceFormatProperties2KHR); WSI_GET_CB(GetPhysicalDeviceImageFormatProperties2); + WSI_GET_CB(GetSemaphoreFdKHR); WSI_GET_CB(ResetFences); WSI_GET_CB(QueueSubmit); WSI_GET_CB(WaitForFences); @@ -349,6 +368,8 @@ wsi_swapchain_finish(struct wsi_swapchain *chain) vk_free(&chain->alloc, chain->buffer_blit_semaphores); } + chain->wsi->DestroySemaphore(chain->device, chain->dma_buf_semaphore, + &chain->alloc); int cmd_pools_count = chain->buffer_blit_queue != VK_NULL_HANDLE ? 1 : chain->wsi->queue_family_count; @@ -843,6 +864,14 @@ wsi_signal_semaphore_for_image(struct vk_device *device, vk_semaphore_reset_temporary(device, semaphore); +#ifndef _WIN32 + VkResult result = wsi_create_sync_for_dma_buf_wait(chain, image, + VK_SYNC_FEATURE_GPU_WAIT, + &semaphore->temporary); + if (result != VK_ERROR_FEATURE_NOT_PRESENT) + return result; +#endif + if (chain->wsi->signal_semaphore_with_memory) { return device->create_sync_for_memory(device, image->memory, false /* signal_memory */, @@ -867,6 +896,14 @@ wsi_signal_fence_for_image(struct vk_device *device, vk_fence_reset_temporary(device, fence); +#ifndef _WIN32 + VkResult result = wsi_create_sync_for_dma_buf_wait(chain, image, + VK_SYNC_FEATURE_CPU_WAIT, + &fence->temporary); + if (result != VK_ERROR_FEATURE_NOT_PRESENT) + return result; +#endif + if (chain->wsi->signal_fence_with_memory) { return device->create_sync_for_memory(device, image->memory, false /* signal_memory */, @@ -1036,16 +1073,50 @@ wsi_common_queue_present(const struct wsi_device *wsi, VkFence fence = swapchain->fences[image_index]; - struct wsi_memory_signal_submit_info mem_signal = { - .sType = VK_STRUCTURE_TYPE_WSI_MEMORY_SIGNAL_SUBMIT_INFO_MESA, - .memory = image->memory, - }; - __vk_append_struct(&submit_info, &mem_signal); + bool has_signal_dma_buf = false; +#ifndef _WIN32 + result = wsi_prepare_signal_dma_buf_from_semaphore(swapchain, image); + if (result == VK_SUCCESS) { + assert(submit_info.signalSemaphoreCount == 0); + submit_info.signalSemaphoreCount = 1; + submit_info.pSignalSemaphores = &swapchain->dma_buf_semaphore; + has_signal_dma_buf = true; + } else if (result == VK_ERROR_FEATURE_NOT_PRESENT) { + result = VK_SUCCESS; + has_signal_dma_buf = false; + } else { + goto fail_present; + } +#endif + + struct wsi_memory_signal_submit_info mem_signal; + if (!has_signal_dma_buf) { + /* If we don't have dma-buf signaling, signal the memory object by + * chaining wsi_memory_signal_submit_info into VkSubmitInfo. + */ + result = VK_SUCCESS; + has_signal_dma_buf = false; + mem_signal = (struct wsi_memory_signal_submit_info) { + .sType = VK_STRUCTURE_TYPE_WSI_MEMORY_SIGNAL_SUBMIT_INFO_MESA, + .memory = image->memory, + }; + __vk_append_struct(&submit_info, &mem_signal); + } result = wsi->QueueSubmit(submit_queue, 1, &submit_info, fence); if (result != VK_SUCCESS) goto fail_present; +#ifndef _WIN32 + if (has_signal_dma_buf) { + result = wsi_signal_dma_buf_from_semaphore(swapchain, image); + if (result != VK_SUCCESS) + goto fail_present; + } +#else + assert(!has_signal_dma_buf); +#endif + if (wsi->sw) wsi->WaitForFences(device, 1, &swapchain->fences[image_index], true, ~0ull); diff --git a/src/vulkan/wsi/wsi_common.h b/src/vulkan/wsi/wsi_common.h index f96632308df..7821e0b0200 100644 --- a/src/vulkan/wsi/wsi_common.h +++ b/src/vulkan/wsi/wsi_common.h @@ -102,6 +102,8 @@ struct wsi_device { VkPhysicalDevicePCIBusInfoPropertiesEXT pci_bus_info; + VkExternalSemaphoreHandleTypeFlags semaphore_export_handle_types; + bool supports_modifiers; uint32_t maxImageDimension2D; VkPresentModeKHR override_present_mode; @@ -209,6 +211,7 @@ struct wsi_device { WSI_CB(GetPhysicalDeviceFormatProperties); WSI_CB(GetPhysicalDeviceFormatProperties2KHR); WSI_CB(GetPhysicalDeviceImageFormatProperties2); + WSI_CB(GetSemaphoreFdKHR); WSI_CB(ResetFences); WSI_CB(QueueSubmit); WSI_CB(WaitForFences); diff --git a/src/vulkan/wsi/wsi_common_drm.c b/src/vulkan/wsi/wsi_common_drm.c index 2f82963841d..ed600606912 100644 --- a/src/vulkan/wsi/wsi_common_drm.c +++ b/src/vulkan/wsi/wsi_common_drm.c @@ -26,16 +26,221 @@ #include "util/macros.h" #include "util/os_file.h" #include "util/xmlconfig.h" +#include "vk_device.h" #include "vk_format.h" +#include "vk_physical_device.h" #include "vk_util.h" #include "drm-uapi/drm_fourcc.h" +#include +#include +#include #include #include #include #include #include +struct dma_buf_export_sync_file_wsi { + __u32 flags; + __s32 fd; +}; + +struct dma_buf_import_sync_file_wsi { + __u32 flags; + __s32 fd; +}; + +#define DMA_BUF_IOCTL_EXPORT_SYNC_FILE_WSI _IOWR(DMA_BUF_BASE, 2, struct dma_buf_export_sync_file_wsi) +#define DMA_BUF_IOCTL_IMPORT_SYNC_FILE_WSI _IOW(DMA_BUF_BASE, 3, struct dma_buf_import_sync_file_wsi) + +static VkResult +wsi_dma_buf_export_sync_file(int dma_buf_fd, int *sync_file_fd) +{ + /* Don't keep trying an IOCTL that doesn't exist. */ + static bool no_dma_buf_sync_file = false; + if (no_dma_buf_sync_file) + return VK_ERROR_FEATURE_NOT_PRESENT; + + struct dma_buf_export_sync_file_wsi export = { + .flags = DMA_BUF_SYNC_RW, + .fd = -1, + }; + int ret = drmIoctl(dma_buf_fd, DMA_BUF_IOCTL_EXPORT_SYNC_FILE_WSI, &export); + if (ret) { + if (errno == ENOTTY) { + no_dma_buf_sync_file = true; + return VK_ERROR_FEATURE_NOT_PRESENT; + } else { + return VK_ERROR_OUT_OF_HOST_MEMORY; + } + } + + *sync_file_fd = export.fd; + + return VK_SUCCESS; +} + +static VkResult +wsi_dma_buf_import_sync_file(int dma_buf_fd, int sync_file_fd) +{ + /* Don't keep trying an IOCTL that doesn't exist. */ + static bool no_dma_buf_sync_file = false; + if (no_dma_buf_sync_file) + return VK_ERROR_FEATURE_NOT_PRESENT; + + struct dma_buf_import_sync_file_wsi import = { + .flags = DMA_BUF_SYNC_RW, + .fd = sync_file_fd, + }; + int ret = drmIoctl(dma_buf_fd, DMA_BUF_IOCTL_IMPORT_SYNC_FILE_WSI, &import); + if (ret) { + if (errno == ENOTTY) { + no_dma_buf_sync_file = true; + return VK_ERROR_FEATURE_NOT_PRESENT; + } else { + return VK_ERROR_OUT_OF_HOST_MEMORY; + } + } + + return VK_SUCCESS; +} + +static VkResult +prepare_signal_dma_buf_from_semaphore(struct wsi_swapchain *chain, + const struct wsi_image *image) +{ + VkResult result; + + if (!(chain->wsi->semaphore_export_handle_types & + VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT)) + return VK_ERROR_FEATURE_NOT_PRESENT; + + int sync_file_fd = -1; + result = wsi_dma_buf_export_sync_file(image->dma_buf_fd, &sync_file_fd); + if (result != VK_SUCCESS) + return result; + + result = wsi_dma_buf_import_sync_file(image->dma_buf_fd, sync_file_fd); + close(sync_file_fd); + if (result != VK_SUCCESS) + return result; + + /* If we got here, all our checks pass. Create the actual semaphore */ + const VkExportSemaphoreCreateInfo export_info = { + .sType = VK_STRUCTURE_TYPE_EXPORT_SEMAPHORE_CREATE_INFO, + .handleTypes = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT, + }; + const VkSemaphoreCreateInfo semaphore_info = { + .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, + .pNext = &export_info, + }; + result = chain->wsi->CreateSemaphore(chain->device, &semaphore_info, + &chain->alloc, + &chain->dma_buf_semaphore); + if (result != VK_SUCCESS) + return result; + + return VK_SUCCESS; +} + +VkResult +wsi_prepare_signal_dma_buf_from_semaphore(struct wsi_swapchain *chain, + const struct wsi_image *image) +{ + VkResult result; + + /* We cache result - 1 in the swapchain */ + if (unlikely(chain->signal_dma_buf_from_semaphore == 0)) { + result = prepare_signal_dma_buf_from_semaphore(chain, image); + assert(result <= 0); + chain->signal_dma_buf_from_semaphore = (int)result - 1; + } else { + result = (VkResult)(chain->signal_dma_buf_from_semaphore + 1); + } + + return result; +} + +VkResult +wsi_signal_dma_buf_from_semaphore(const struct wsi_swapchain *chain, + const struct wsi_image *image) +{ + VkResult result; + + const VkSemaphoreGetFdInfoKHR get_fd_info = { + .sType = VK_STRUCTURE_TYPE_SEMAPHORE_GET_FD_INFO_KHR, + .semaphore = chain->dma_buf_semaphore, + .handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT, + }; + int sync_file_fd = -1; + result = chain->wsi->GetSemaphoreFdKHR(chain->device, &get_fd_info, + &sync_file_fd); + if (result != VK_SUCCESS) + return result; + + result = wsi_dma_buf_import_sync_file(image->dma_buf_fd, sync_file_fd); + close(sync_file_fd); + return result; +} + +static const struct vk_sync_type * +get_sync_file_sync_type(struct vk_device *device, + enum vk_sync_features req_features) +{ + for (const struct vk_sync_type *const *t = + device->physical->supported_sync_types; *t; t++) { + if (req_features & ~(*t)->features) + continue; + + if ((*t)->import_sync_file != NULL) + return *t; + } + + return NULL; +} + +VkResult +wsi_create_sync_for_dma_buf_wait(const struct wsi_swapchain *chain, + const struct wsi_image *image, + enum vk_sync_features req_features, + struct vk_sync **sync_out) +{ + VK_FROM_HANDLE(vk_device, device, chain->device); + VkResult result; + + const struct vk_sync_type *sync_type = + get_sync_file_sync_type(device, req_features); + if (sync_type == NULL) + return VK_ERROR_FEATURE_NOT_PRESENT; + + int sync_file_fd = -1; + result = wsi_dma_buf_export_sync_file(image->dma_buf_fd, &sync_file_fd); + if (result != VK_SUCCESS) + return result; + + struct vk_sync *sync = NULL; + result = vk_sync_create(device, sync_type, VK_SYNC_IS_SHAREABLE, 0, &sync); + if (result != VK_SUCCESS) + goto fail_close_sync_file; + + result = vk_sync_import_sync_file(device, sync, sync_file_fd); + if (result != VK_SUCCESS) + goto fail_destroy_sync; + + close(sync_file_fd); + *sync_out = sync; + + return VK_SUCCESS; + +fail_destroy_sync: + vk_sync_destroy(device, sync); +fail_close_sync_file: + close(sync_file_fd); + + return result; +} + bool wsi_common_drm_devices_equal(int fd_a, int fd_b) { diff --git a/src/vulkan/wsi/wsi_common_private.h b/src/vulkan/wsi/wsi_common_private.h index b5198d45b0b..e5d6bb5204b 100644 --- a/src/vulkan/wsi/wsi_common_private.h +++ b/src/vulkan/wsi/wsi_common_private.h @@ -25,6 +25,7 @@ #include "wsi_common.h" #include "vulkan/runtime/vk_object.h" +#include "vulkan/runtime/vk_sync.h" struct wsi_image; struct wsi_swapchain; @@ -97,6 +98,9 @@ struct wsi_swapchain { VkSemaphore* buffer_blit_semaphores; VkPresentModeKHR present_mode; + int signal_dma_buf_from_semaphore; + VkSemaphore dma_buf_semaphore; + struct wsi_image_info image_info; uint32_t image_count; @@ -193,6 +197,17 @@ void wsi_destroy_image(const struct wsi_swapchain *chain, struct wsi_image *image); +VkResult +wsi_prepare_signal_dma_buf_from_semaphore(struct wsi_swapchain *chain, + const struct wsi_image *image); +VkResult +wsi_signal_dma_buf_from_semaphore(const struct wsi_swapchain *chain, + const struct wsi_image *image); +VkResult +wsi_create_sync_for_dma_buf_wait(const struct wsi_swapchain *chain, + const struct wsi_image *image, + enum vk_sync_features sync_features, + struct vk_sync **sync_out); struct wsi_interface { VkResult (*get_support)(VkIcdSurfaceBase *surface,