vulkan/wsi: Implement prime in a completely generic way
[mesa.git] / src / vulkan / wsi / wsi_common.c
index bb35237fb9206c3fd1aa3be2328a5d4dbae66295..7cb5d894e772f0f3f004bbc5368e4d12029fd58c 100644 (file)
@@ -22,6 +22,7 @@
  */
 
 #include "wsi_common_private.h"
+#include "util/macros.h"
 
 void
 wsi_device_init(struct wsi_device *wsi,
@@ -29,25 +30,363 @@ wsi_device_init(struct wsi_device *wsi,
                 WSI_FN_GetPhysicalDeviceProcAddr proc_addr)
 {
    memset(wsi, 0, sizeof(*wsi));
+
+#define WSI_GET_CB(func) \
+   PFN_vk##func func = (PFN_vk##func)proc_addr(pdevice, "vk" #func)
+   WSI_GET_CB(GetPhysicalDeviceMemoryProperties);
+   WSI_GET_CB(GetPhysicalDeviceQueueFamilyProperties);
+#undef WSI_GET_CB
+
+   GetPhysicalDeviceMemoryProperties(pdevice, &wsi->memory_props);
+   GetPhysicalDeviceQueueFamilyProperties(pdevice, &wsi->queue_family_count, NULL);
+
+#define WSI_GET_CB(func) \
+   wsi->func = (PFN_vk##func)proc_addr(pdevice, "vk" #func)
+   WSI_GET_CB(AllocateMemory);
+   WSI_GET_CB(AllocateCommandBuffers);
+   WSI_GET_CB(BindBufferMemory);
+   WSI_GET_CB(BindImageMemory);
+   WSI_GET_CB(BeginCommandBuffer);
+   WSI_GET_CB(CmdCopyImageToBuffer);
+   WSI_GET_CB(CreateBuffer);
+   WSI_GET_CB(CreateCommandPool);
+   WSI_GET_CB(CreateImage);
+   WSI_GET_CB(DestroyBuffer);
+   WSI_GET_CB(DestroyCommandPool);
+   WSI_GET_CB(DestroyImage);
+   WSI_GET_CB(EndCommandBuffer);
+   WSI_GET_CB(FreeMemory);
+   WSI_GET_CB(FreeCommandBuffers);
+   WSI_GET_CB(GetBufferMemoryRequirements);
+   WSI_GET_CB(GetImageMemoryRequirements);
+   WSI_GET_CB(GetMemoryFdKHR);
+   WSI_GET_CB(QueueSubmit);
+#undef WSI_GET_CB
 }
 
 VkResult
 wsi_swapchain_init(const struct wsi_device *wsi,
                    struct wsi_swapchain *chain,
                    VkDevice device,
-                   const VkSwapchainCreateInfoKHRpCreateInfo,
+                   const VkSwapchainCreateInfoKHR *pCreateInfo,
                    const VkAllocationCallbacks *pAllocator)
 {
+   VkResult result;
+
    memset(chain, 0, sizeof(*chain));
 
    chain->wsi = wsi;
    chain->device = device;
    chain->alloc = *pAllocator;
 
+   chain->cmd_pools =
+      vk_zalloc(pAllocator, sizeof(VkCommandPool) * wsi->queue_family_count, 8,
+                VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
+   if (!chain->cmd_pools)
+      return VK_ERROR_OUT_OF_HOST_MEMORY;
+
+   for (uint32_t i = 0; i < wsi->queue_family_count; i++) {
+      const VkCommandPoolCreateInfo cmd_pool_info = {
+         .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
+         .pNext = NULL,
+         .flags = 0,
+         .queueFamilyIndex = i,
+      };
+      result = wsi->CreateCommandPool(device, &cmd_pool_info, &chain->alloc,
+                                      &chain->cmd_pools[i]);
+      if (result != VK_SUCCESS)
+         goto fail;
+   }
+
    return VK_SUCCESS;
+
+fail:
+   wsi_swapchain_finish(chain);
+   return result;
 }
 
 void
 wsi_swapchain_finish(struct wsi_swapchain *chain)
 {
+   for (uint32_t i = 0; i < chain->wsi->queue_family_count; i++) {
+      chain->wsi->DestroyCommandPool(chain->device, chain->cmd_pools[i],
+                                     &chain->alloc);
+   }
+}
+
+static uint32_t
+select_memory_type(const struct wsi_device *wsi,
+                   VkMemoryPropertyFlags props,
+                   uint32_t type_bits)
+{
+   for (uint32_t i = 0; i < wsi->memory_props.memoryTypeCount; i++) {
+       const VkMemoryType type = wsi->memory_props.memoryTypes[i];
+       if ((type_bits & (1 << i)) && (type.propertyFlags & props) == props)
+         return i;
+   }
+
+   unreachable("No memory type found");
+}
+
+static uint32_t
+vk_format_size(VkFormat format)
+{
+   switch (format) {
+   case VK_FORMAT_B8G8R8A8_UNORM:
+   case VK_FORMAT_B8G8R8A8_SRGB:
+      return 4;
+   default:
+      unreachable("Unknown WSI Format");
+   }
+}
+
+static inline uint32_t
+align_u32(uint32_t v, uint32_t a)
+{
+   assert(a != 0 && a == (a & -a));
+   return (v + a - 1) & ~(a - 1);
+}
+
+#define WSI_PRIME_LINEAR_STRIDE_ALIGN 256
+
+VkResult
+wsi_create_prime_image(const struct wsi_swapchain *chain,
+                       const VkSwapchainCreateInfoKHR *pCreateInfo,
+                       struct wsi_image *image)
+{
+   const struct wsi_device *wsi = chain->wsi;
+   VkResult result;
+
+   memset(image, 0, sizeof(*image));
+
+   const uint32_t cpp = vk_format_size(pCreateInfo->imageFormat);
+   const uint32_t linear_stride = align_u32(pCreateInfo->imageExtent.width * cpp,
+                                            WSI_PRIME_LINEAR_STRIDE_ALIGN);
+
+   uint32_t linear_size = linear_stride * pCreateInfo->imageExtent.height;
+   linear_size = align_u32(linear_size, 4096);
+
+   const VkExternalMemoryBufferCreateInfoKHR prime_buffer_external_info = {
+      .sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_BUFFER_CREATE_INFO_KHR,
+      .pNext = NULL,
+      .handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT,
+   };
+   const VkBufferCreateInfo prime_buffer_info = {
+      .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
+      .pNext = &prime_buffer_external_info,
+      .size = linear_size,
+      .usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT,
+      .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
+   };
+   result = wsi->CreateBuffer(chain->device, &prime_buffer_info,
+                              &chain->alloc, &image->prime.buffer);
+   if (result != VK_SUCCESS)
+      goto fail;
+
+   VkMemoryRequirements reqs;
+   wsi->GetBufferMemoryRequirements(chain->device, image->prime.buffer, &reqs);
+   assert(reqs.size <= linear_size);
+
+   const struct wsi_memory_allocate_info memory_wsi_info = {
+      .sType = VK_STRUCTURE_TYPE_WSI_MEMORY_ALLOCATE_INFO_MESA,
+      .pNext = NULL,
+      .implicit_sync = true,
+   };
+   const VkExportMemoryAllocateInfoKHR prime_memory_export_info = {
+      .sType = VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO_KHR,
+      .pNext = &memory_wsi_info,
+      .handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT,
+   };
+   const VkMemoryDedicatedAllocateInfoKHR prime_memory_dedicated_info = {
+      .sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO_KHR,
+      .pNext = &prime_memory_export_info,
+      .image = VK_NULL_HANDLE,
+      .buffer = image->prime.buffer,
+   };
+   const VkMemoryAllocateInfo prime_memory_info = {
+      .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
+      .pNext = &prime_memory_dedicated_info,
+      .allocationSize = linear_size,
+      .memoryTypeIndex = select_memory_type(wsi, 0, reqs.memoryTypeBits),
+   };
+   result = wsi->AllocateMemory(chain->device, &prime_memory_info,
+                                &chain->alloc, &image->prime.memory);
+   if (result != VK_SUCCESS)
+      goto fail;
+
+   result = wsi->BindBufferMemory(chain->device, image->prime.buffer,
+                                  image->prime.memory, 0);
+   if (result != VK_SUCCESS)
+      goto fail;
+
+   const VkImageCreateInfo image_info = {
+      .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
+      .pNext = NULL,
+      .flags = 0,
+      .imageType = VK_IMAGE_TYPE_2D,
+      .format = pCreateInfo->imageFormat,
+      .extent = {
+         .width = pCreateInfo->imageExtent.width,
+         .height = pCreateInfo->imageExtent.height,
+         .depth = 1,
+      },
+      .mipLevels = 1,
+      .arrayLayers = 1,
+      .samples = VK_SAMPLE_COUNT_1_BIT,
+      .tiling = VK_IMAGE_TILING_OPTIMAL,
+      .usage = pCreateInfo->imageUsage | VK_IMAGE_USAGE_TRANSFER_SRC_BIT,
+      .sharingMode = pCreateInfo->imageSharingMode,
+      .queueFamilyIndexCount = pCreateInfo->queueFamilyIndexCount,
+      .pQueueFamilyIndices = pCreateInfo->pQueueFamilyIndices,
+      .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
+   };
+   result = wsi->CreateImage(chain->device, &image_info,
+                             &chain->alloc, &image->image);
+   if (result != VK_SUCCESS)
+      goto fail;
+
+   wsi->GetImageMemoryRequirements(chain->device, image->image, &reqs);
+
+   const VkMemoryDedicatedAllocateInfoKHR memory_dedicated_info = {
+      .sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO_KHR,
+      .pNext = NULL,
+      .image = image->image,
+      .buffer = VK_NULL_HANDLE,
+   };
+   const VkMemoryAllocateInfo memory_info = {
+      .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
+      .pNext = &memory_dedicated_info,
+      .allocationSize = reqs.size,
+      .memoryTypeIndex = select_memory_type(wsi, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
+                                            reqs.memoryTypeBits),
+   };
+   result = wsi->AllocateMemory(chain->device, &memory_info,
+                                &chain->alloc, &image->memory);
+   if (result != VK_SUCCESS)
+      goto fail;
+
+   result = wsi->BindImageMemory(chain->device, image->image,
+                                 image->memory, 0);
+   if (result != VK_SUCCESS)
+      goto fail;
+
+   image->prime.blit_cmd_buffers =
+      vk_zalloc(&chain->alloc,
+                sizeof(VkCommandBuffer) * wsi->queue_family_count, 8,
+                VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
+   if (!image->prime.blit_cmd_buffers)
+      goto fail;
+
+   for (uint32_t i = 0; i < wsi->queue_family_count; i++) {
+      const VkCommandBufferAllocateInfo cmd_buffer_info = {
+         .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
+         .pNext = NULL,
+         .commandPool = chain->cmd_pools[i],
+         .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
+         .commandBufferCount = 1,
+      };
+      result = wsi->AllocateCommandBuffers(chain->device, &cmd_buffer_info,
+                                           &image->prime.blit_cmd_buffers[i]);
+      if (result != VK_SUCCESS)
+         goto fail;
+
+      const VkCommandBufferBeginInfo begin_info = {
+         .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
+      };
+      wsi->BeginCommandBuffer(image->prime.blit_cmd_buffers[i], &begin_info);
+
+      struct VkBufferImageCopy buffer_image_copy = {
+         .bufferOffset = 0,
+         .bufferRowLength = linear_stride / cpp,
+         .bufferImageHeight = 0,
+         .imageSubresource = {
+            .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
+            .mipLevel = 0,
+            .baseArrayLayer = 0,
+            .layerCount = 1,
+         },
+         .imageOffset = { .x = 0, .y = 0, .z = 0 },
+         .imageExtent = {
+            .width = pCreateInfo->imageExtent.width,
+            .height = pCreateInfo->imageExtent.height,
+            .depth = 1,
+         },
+      };
+      wsi->CmdCopyImageToBuffer(image->prime.blit_cmd_buffers[i],
+                                image->image,
+                                VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
+                                image->prime.buffer,
+                                1, &buffer_image_copy);
+
+      result = wsi->EndCommandBuffer(image->prime.blit_cmd_buffers[i]);
+      if (result != VK_SUCCESS)
+         goto fail;
+   }
+
+   const VkMemoryGetFdInfoKHR linear_memory_get_fd_info = {
+      .sType = VK_STRUCTURE_TYPE_MEMORY_GET_FD_INFO_KHR,
+      .pNext = NULL,
+      .memory = image->prime.memory,
+      .handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT,
+   };
+   int fd;
+   result = wsi->GetMemoryFdKHR(chain->device, &linear_memory_get_fd_info, &fd);
+   if (result != VK_SUCCESS)
+      goto fail;
+
+   image->size = linear_size;
+   image->row_pitch = linear_stride;
+   image->offset = 0;
+   image->fd = fd;
+
+   return VK_SUCCESS;
+
+fail:
+   wsi_destroy_prime_image(chain, image);
+
+   return result;
+}
+
+void
+wsi_destroy_prime_image(const struct wsi_swapchain *chain,
+                        struct wsi_image *image)
+{
+   const struct wsi_device *wsi = chain->wsi;
+
+   if (image->prime.blit_cmd_buffers) {
+      for (uint32_t i = 0; i < wsi->queue_family_count; i++) {
+         wsi->FreeCommandBuffers(chain->device, chain->cmd_pools[i],
+                                 1, &image->prime.blit_cmd_buffers[i]);
+      }
+      vk_free(&chain->alloc, image->prime.blit_cmd_buffers);
+   }
+
+   wsi->FreeMemory(chain->device, image->memory, &chain->alloc);
+   wsi->DestroyImage(chain->device, image->image, &chain->alloc);
+   wsi->FreeMemory(chain->device, image->prime.memory, &chain->alloc);
+   wsi->DestroyBuffer(chain->device, image->prime.buffer, &chain->alloc);
+}
+
+VkResult
+wsi_prime_image_blit_to_linear(const struct wsi_swapchain *chain,
+                               struct wsi_image *image,
+                               VkQueue queue,
+                               uint32_t waitSemaphoreCount,
+                               const VkSemaphore *pWaitSemaphores)
+{
+   uint32_t queue_family = chain->wsi->queue_get_family_index(queue);
+
+   VkPipelineStageFlags stage_flags = VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT;
+   const VkSubmitInfo submit_info = {
+      .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
+      .pNext = NULL,
+      .waitSemaphoreCount = waitSemaphoreCount,
+      .pWaitSemaphores = pWaitSemaphores,
+      .pWaitDstStageMask = &stage_flags,
+      .commandBufferCount = 1,
+      .pCommandBuffers = &image->prime.blit_cmd_buffers[queue_family],
+      .signalSemaphoreCount = 0,
+      .pSignalSemaphores = NULL,
+   };
+   return chain->wsi->QueueSubmit(queue, 1, &submit_info, VK_NULL_HANDLE);
 }