[Mesa-dev] [PATCH 18/21] anv: Implement support for exporting semaphores as FENCE_FD

Chad Versace chadversary at chromium.org
Wed May 3 00:17:39 UTC 2017


I'm stopping here for today and will resume tomorrow.

On Fri 14 Apr 2017, Jason Ekstrand wrote:
> ---
>  src/intel/vulkan/anv_batch_chain.c | 96 ++++++++++++++++++++++++++++++++++++--
>  src/intel/vulkan/anv_device.c      | 25 ++++++++++
>  src/intel/vulkan/anv_gem.c         | 36 ++++++++++++++
>  src/intel/vulkan/anv_private.h     | 24 +++++++---
>  src/intel/vulkan/anv_queue.c       | 73 +++++++++++++++++++++++++++--
>  5 files changed, 240 insertions(+), 14 deletions(-)
> 
> diff --git a/src/intel/vulkan/anv_batch_chain.c b/src/intel/vulkan/anv_batch_chain.c
> index 0529f22..ec37c81 100644
> --- a/src/intel/vulkan/anv_batch_chain.c
> +++ b/src/intel/vulkan/anv_batch_chain.c
> @@ -1387,6 +1387,23 @@ setup_execbuf_for_cmd_buffer(struct anv_execbuf *execbuf,
>     return VK_SUCCESS;
>  }
>  
> +static void
> +setup_empty_execbuf(struct anv_execbuf *execbuf, struct anv_device *device)
> +{
> +   anv_execbuf_add_bo(execbuf, &device->trivial_batch_bo, NULL, 0,
> +                      &device->alloc);
> +
> +   execbuf->execbuf = (struct drm_i915_gem_execbuffer2) {
> +      .buffers_ptr = (uintptr_t) execbuf->objects,
> +      .buffer_count = execbuf->bo_count,
> +      .batch_start_offset = 0,
> +      .batch_len = 8, /* GEN8_MI_BATCH_BUFFER_END and NOOP */
> +      .flags = I915_EXEC_HANDLE_LUT | I915_EXEC_RENDER,
> +      .rsvd1 = device->context_id,
> +      .rsvd2 = 0,
> +   };
> +}
> +
>  VkResult
>  anv_cmd_buffer_execbuf(struct anv_device *device,
>                         struct anv_cmd_buffer *cmd_buffer,
> @@ -1398,11 +1415,13 @@ anv_cmd_buffer_execbuf(struct anv_device *device,
>     struct anv_execbuf execbuf;
>     anv_execbuf_init(&execbuf);
>  
> +   int in_fence = -1;
>     VkResult result = VK_SUCCESS;
>     for (uint32_t i = 0; i < num_in_semaphores; i++) {
>        ANV_FROM_HANDLE(anv_semaphore, semaphore, in_semaphores[i]);
> -      assert(semaphore->temporary.type == ANV_SEMAPHORE_TYPE_NONE);
> -      struct anv_semaphore_impl *impl = &semaphore->permanent;
> +      struct anv_semaphore_impl *impl =
> +         semaphore->temporary.type != ANV_SEMAPHORE_TYPE_NONE ?
> +         &semaphore->temporary : &semaphore->permanent;
>  
>        switch (impl->type) {
>        case ANV_SEMAPHORE_TYPE_BO:
> @@ -1411,13 +1430,42 @@ anv_cmd_buffer_execbuf(struct anv_device *device,
>           if (result != VK_SUCCESS)
>              return result;
>           break;
> +
> +      case ANV_SEMAPHORE_TYPE_SYNC_FILE:
> +         if (in_fence == -1) {
> +            in_fence = impl->fd;
> +         } else {
> +            int merge = anv_gem_sync_file_merge(device, in_fence, impl->fd);
> +            if (merge == -1)
> +               return vk_error(VK_ERROR_INVALID_EXTERNAL_HANDLE_KHX);
> +
> +            close(impl->fd);
> +            close(in_fence);
> +            in_fence = merge;
> +         }
> +
> +         impl->fd = -1;
> +         break;
> +
>        default:
>           break;
>        }
> +
> +      /* Waiting on a semaphore with temporary state implicitly resets it back
> +       * to the permanent state.
> +       */
> +      if (semaphore->temporary.type != ANV_SEMAPHORE_TYPE_NONE) {
> +         assert(semaphore->temporary.type == ANV_SEMAPHORE_TYPE_SYNC_FILE);
> +         semaphore->temporary.type = ANV_SEMAPHORE_TYPE_NONE;
> +      }
>     }
>  
> +   bool need_out_fence = false;
>     for (uint32_t i = 0; i < num_out_semaphores; i++) {
>        ANV_FROM_HANDLE(anv_semaphore, semaphore, out_semaphores[i]);
> +      /* Out fences can't have temporary state because that would imply
> +       * that we imported a sync file and are trying to signal it.
> +       */
>        assert(semaphore->temporary.type == ANV_SEMAPHORE_TYPE_NONE);
>        struct anv_semaphore_impl *impl = &semaphore->permanent;
>  
> @@ -1428,17 +1476,55 @@ anv_cmd_buffer_execbuf(struct anv_device *device,
>           if (result != VK_SUCCESS)
>              return result;
>           break;
> +
> +      case ANV_SEMAPHORE_TYPE_SYNC_FILE:
> +         need_out_fence = true;
> +         break;
> +
>        default:
>           break;
>        }
>     }
>  
> -   result = setup_execbuf_for_cmd_buffer(&execbuf, cmd_buffer);
> -   if (result != VK_SUCCESS)
> -      return result;
> +   if (cmd_buffer) {
> +      result = setup_execbuf_for_cmd_buffer(&execbuf, cmd_buffer);
> +      if (result != VK_SUCCESS)
> +         return result;
> +   } else {
> +      setup_empty_execbuf(&execbuf, device);
> +   }
> +
> +   if (in_fence != -1) {
> +      execbuf.execbuf.flags |= I915_EXEC_FENCE_IN;
> +      execbuf.execbuf.rsvd2 |= (uint32_t)in_fence;
> +   }
> +
> +   if (need_out_fence)
> +      execbuf.execbuf.flags |= I915_EXEC_FENCE_OUT;
>  
>     result = anv_device_execbuf(device, &execbuf.execbuf, execbuf.bos);
>  
> +   /* Execbuf does not consume the in_fence.  It's our job to close it. */
> +   close(in_fence);
> +
> +   if (result == VK_SUCCESS && need_out_fence) {
> +      int out_fence = execbuf.execbuf.rsvd2 >> 32;
> +      for (uint32_t i = 0; i < num_out_semaphores; i++) {
> +         ANV_FROM_HANDLE(anv_semaphore, semaphore, out_semaphores[i]);
> +         /* Out fences can't have temporary state because that would imply
> +          * that we imported a sync file and are trying to signal it.
> +          */
> +         assert(semaphore->temporary.type == ANV_SEMAPHORE_TYPE_NONE);
> +         struct anv_semaphore_impl *impl = &semaphore->permanent;
> +
> +         if (impl->type == ANV_SEMAPHORE_TYPE_SYNC_FILE) {
> +            assert(impl->fd == -1);
> +            impl->fd = dup(out_fence);
> +         }
> +      }
> +      close(out_fence);
> +   }
> +
>     anv_execbuf_finish(&execbuf, &device->alloc);
>  
>     return result;
> diff --git a/src/intel/vulkan/anv_device.c b/src/intel/vulkan/anv_device.c
> index f6e77ab..2885bb6 100644
> --- a/src/intel/vulkan/anv_device.c
> +++ b/src/intel/vulkan/anv_device.c
> @@ -232,6 +232,7 @@ anv_physical_device_init(struct anv_physical_device *device,
>        goto fail;
>  
>     device->has_exec_async = anv_gem_get_param(fd, I915_PARAM_HAS_EXEC_ASYNC);
> +   device->has_exec_fence = anv_gem_get_param(fd, I915_PARAM_HAS_EXEC_FENCE);
>  
>     bool swizzled = anv_gem_get_bit6_swizzle(fd, I915_TILING_X);
>  
> @@ -993,6 +994,26 @@ anv_device_init_border_colors(struct anv_device *device)
>                                                      border_colors);
>  }
>  
> +static void
> +anv_device_init_trivial_batch(struct anv_device *device)
> +{
> +   anv_bo_init_new(&device->trivial_batch_bo, device, 4096);
> +   void *map = anv_gem_mmap(device, device->trivial_batch_bo.gem_handle,
> +                            0, 4096, 0);
> +
> +   struct anv_batch batch;
> +   batch.start = batch.next = map;
> +   batch.end = map + 4096;
> +
> +   anv_batch_emit(&batch, GEN7_MI_BATCH_BUFFER_END, bbe);
> +   anv_batch_emit(&batch, GEN7_MI_NOOP, noop);
> +
> +   if (!device->info.has_llc)
> +      anv_clflush_range(map, batch.next - map);
> +
> +   anv_gem_munmap(map, device->trivial_batch_bo.size);
> +}
> +
>  VkResult anv_CreateDevice(
>      VkPhysicalDevice                            physicalDevice,
>      const VkDeviceCreateInfo*                   pCreateInfo,
> @@ -1116,6 +1137,8 @@ VkResult anv_CreateDevice(
>     if (result != VK_SUCCESS)
>        goto fail_surface_state_pool;
>  
> +   anv_device_init_trivial_batch(device);
> +
>     anv_scratch_pool_init(device, &device->scratch_pool);
>  
>     anv_queue_init(device, &device->queue);
> @@ -1205,6 +1228,8 @@ void anv_DestroyDevice(
>     anv_gem_munmap(device->workaround_bo.map, device->workaround_bo.size);
>     anv_gem_close(device, device->workaround_bo.gem_handle);
>  
> +   anv_gem_close(device, device->trivial_batch_bo.gem_handle);
> +
>     anv_state_pool_finish(&device->surface_state_pool);
>     anv_block_pool_finish(&device->surface_state_block_pool);
>     anv_state_pool_finish(&device->instruction_state_pool);
> diff --git a/src/intel/vulkan/anv_gem.c b/src/intel/vulkan/anv_gem.c
> index 1392bf4..ffdc5a1 100644
> --- a/src/intel/vulkan/anv_gem.c
> +++ b/src/intel/vulkan/anv_gem.c
> @@ -22,6 +22,7 @@
>   */
>  
>  #include <sys/ioctl.h>
> +#include <sys/types.h>
>  #include <sys/mman.h>
>  #include <string.h>
>  #include <errno.h>
> @@ -400,3 +401,38 @@ anv_gem_fd_to_handle(struct anv_device *device, int fd)
>  
>     return args.handle;
>  }
> +
> +#ifndef SYNC_IOC_MAGIC
> +/* duplicated from linux/sync_file.h to avoid build-time depnedency
> + * on new (v4.7) kernel headers.  Once distro's are mostly using
> + * something newer than v4.7 drop this and #include <linux/sync_file.h>
> + * instead.
> + */
> +struct sync_merge_data {
> +   char  name[32];
> +   __s32 fd2;
> +   __s32 fence;
> +   __u32 flags;
> +   __u32 pad;
> +};
> +
> +#define SYNC_IOC_MAGIC '>'
> +#define SYNC_IOC_MERGE _IOWR(SYNC_IOC_MAGIC, 3, struct sync_merge_data)
> +#endif
> +
> +int
> +anv_gem_sync_file_merge(struct anv_device *device, int fd1, int fd2)
> +{
> +   const char name[] = "anv merge fence";
> +   struct sync_merge_data args = {
> +      .fd2 = fd2,
> +      .fence = -1,
> +   };
> +   memcpy(args.name, name, sizeof(name));
> +
> +   int ret = anv_ioctl(fd1, SYNC_IOC_MERGE, &args);
> +   if (ret == -1)
> +      return -1;
> +
> +   return args.fence;
> +}
> diff --git a/src/intel/vulkan/anv_private.h b/src/intel/vulkan/anv_private.h
> index 4a59cac..a083a07 100644
> --- a/src/intel/vulkan/anv_private.h
> +++ b/src/intel/vulkan/anv_private.h
> @@ -647,6 +647,7 @@ struct anv_physical_device {
>      struct isl_device                           isl_dev;
>      int                                         cmd_parser_version;
>      bool                                        has_exec_async;
> +    bool                                        has_exec_fence;
>  
>      uint32_t                                    eu_total;
>      uint32_t                                    subslice_total;
> @@ -733,6 +734,7 @@ struct anv_device {
>      struct anv_state_pool                       surface_state_pool;
>  
>      struct anv_bo                               workaround_bo;
> +    struct anv_bo                               trivial_batch_bo;
>  
>      struct anv_pipeline_cache                   blorp_shader_cache;
>      struct blorp_context                        blorp;
> @@ -797,6 +799,7 @@ uint32_t anv_gem_fd_to_handle(struct anv_device *device, int fd);
>  int anv_gem_set_caching(struct anv_device *device, uint32_t gem_handle, uint32_t caching);
>  int anv_gem_set_domain(struct anv_device *device, uint32_t gem_handle,
>                         uint32_t read_domains, uint32_t write_domain);
> +int anv_gem_sync_file_merge(struct anv_device *device, int fd1, int fd2);
>  
>  VkResult anv_bo_init_new(struct anv_bo *bo, struct anv_device *device, uint64_t size);
>  
> @@ -1714,17 +1717,26 @@ enum anv_semaphore_type {
>     ANV_SEMAPHORE_TYPE_NONE = 0,
>     ANV_SEMAPHORE_TYPE_DUMMY,
>     ANV_SEMAPHORE_TYPE_BO,
> +   ANV_SEMAPHORE_TYPE_SYNC_FILE,
>  };
>  
>  struct anv_semaphore_impl {
>     enum anv_semaphore_type type;
>  
> -   /* A BO representing this semaphore when type == ANV_SEMAPHORE_TYPE_BO.
> -    * This BO will be added to the object list on any execbuf2 calls for
> -    * which this semaphore is used as a wait or signal fence.  When used as
> -    * a signal fence, the EXEC_OBJECT_WRITE flag will be set.
> -    */
> -   struct anv_bo *bo;
> +   union {
> +      /* A BO representing this semaphore when type == ANV_SEMAPHORE_TYPE_BO.
> +       * This BO will be added to the object list on any execbuf2 calls for
> +       * which this semaphore is used as a wait or signal fence.  When used as
> +       * a signal fence, the EXEC_OBJECT_WRITE flag will be set.
> +       */
> +      struct anv_bo *bo;
> +
> +      /* The sync file descriptor when type == AKV_SEMAPHORE_TYPE_SYNC_FILE.
> +       * If the semaphore is in the unsignaled state due to either just being
> +       * created or because it has been used for a wait, fd will be -1.
> +       */
> +      int fd;
> +   };
>  };
>  
>  struct anv_semaphore {
> diff --git a/src/intel/vulkan/anv_queue.c b/src/intel/vulkan/anv_queue.c
> index 980fd2d..e7c8ef5 100644
> --- a/src/intel/vulkan/anv_queue.c
> +++ b/src/intel/vulkan/anv_queue.c
> @@ -159,6 +159,23 @@ VkResult anv_QueueSubmit(
>     pthread_mutex_lock(&device->mutex);
>  
>     for (uint32_t i = 0; i < submitCount; i++) {
> +      if (pSubmits[i].commandBufferCount == 0) {
> +         /* If we don't have any command buffers, we need to submit a dummy
> +          * batch to give GEM something to wait on.  We could, potentially,
> +          * come up with something more efficient but this shouldn't be a
> +          * common case.
> +          */
> +         result = anv_cmd_buffer_execbuf(device, NULL,
> +                                         pSubmits[i].pWaitSemaphores,
> +                                         pSubmits[i].waitSemaphoreCount,
> +                                         pSubmits[i].pSignalSemaphores,
> +                                         pSubmits[i].signalSemaphoreCount);
> +         if (result != VK_SUCCESS)
> +            goto out;
> +
> +         continue;
> +      }
> +
>        for (uint32_t j = 0; j < pSubmits[i].commandBufferCount; j++) {
>           ANV_FROM_HANDLE(anv_cmd_buffer, cmd_buffer,
>                           pSubmits[i].pCommandBuffers[j]);
> @@ -554,6 +571,11 @@ VkResult anv_CreateSemaphore(
>         * EXEC_OBJECT_ASYNC bit set.
>         */
>        semaphore->permanent.bo->flags &= ~EXEC_OBJECT_ASYNC;
> +   } else if (handleTypes & VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_FENCE_FD_BIT_KHX) {
> +      assert(handleTypes == VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_FENCE_FD_BIT_KHX);
> +
> +      semaphore->permanent.type = ANV_SEMAPHORE_TYPE_SYNC_FILE;
> +      semaphore->permanent.fd = -1;
>     } else {
>        assert(!"Unknown handle type");
>        vk_free2(&device->alloc, pAllocator, semaphore);
> @@ -581,6 +603,10 @@ anv_semaphore_impl_cleanup(struct anv_device *device,
>        anv_bo_cache_release(device, &device->bo_cache, impl->bo);
>        break;
>  
> +   case ANV_SEMAPHORE_TYPE_SYNC_FILE:
> +      close(impl->fd);
> +      break;
> +
>     default:
>        unreachable("Invalid semaphore type");
>     }
> @@ -608,6 +634,8 @@ void anv_GetPhysicalDeviceExternalSemaphorePropertiesKHX(
>      const VkPhysicalDeviceExternalSemaphoreInfoKHX* pExternalSemaphoreInfo,
>      VkExternalSemaphorePropertiesKHX*           pExternalSemaphoreProperties)
>  {
> +   ANV_FROM_HANDLE(anv_physical_device, device, physicalDevice);
> +
>     switch (pExternalSemaphoreInfo->handleType) {
>     case VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT_KHX:
>        pExternalSemaphoreProperties->exportFromImportedHandleTypes = 0;
> @@ -616,13 +644,27 @@ void anv_GetPhysicalDeviceExternalSemaphorePropertiesKHX(
>        pExternalSemaphoreProperties->externalSemaphoreFeatures =
>           VK_EXTERNAL_SEMAPHORE_FEATURE_EXPORTABLE_BIT_KHX |
>           VK_EXTERNAL_SEMAPHORE_FEATURE_IMPORTABLE_BIT_KHX;
> +      return;
> +
> +   case VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_FENCE_FD_BIT_KHX:
> +      if (device->has_exec_fence) {
> +         pExternalSemaphoreProperties->exportFromImportedHandleTypes = 0;
> +         pExternalSemaphoreProperties->compatibleHandleTypes =
> +            VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_FENCE_FD_BIT_KHX;
> +         pExternalSemaphoreProperties->externalSemaphoreFeatures =
> +            VK_EXTERNAL_SEMAPHORE_FEATURE_EXPORTABLE_BIT_KHX |
> +            VK_EXTERNAL_SEMAPHORE_FEATURE_IMPORTABLE_BIT_KHX;
> +         return;
> +      }
>        break;
>  
>     default:
> -      pExternalSemaphoreProperties->exportFromImportedHandleTypes = 0;
> -      pExternalSemaphoreProperties->compatibleHandleTypes = 0;
> -      pExternalSemaphoreProperties->externalSemaphoreFeatures = 0;
> +      break;
>     }
> +
> +   pExternalSemaphoreProperties->exportFromImportedHandleTypes = 0;
> +   pExternalSemaphoreProperties->compatibleHandleTypes = 0;
> +   pExternalSemaphoreProperties->externalSemaphoreFeatures = 0;
>  }
>  
>  VkResult anv_ImportSemaphoreFdKHX(
> @@ -654,6 +696,14 @@ VkResult anv_ImportSemaphoreFdKHX(
>        return VK_SUCCESS;
>     }
>  
> +   case VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_FENCE_FD_BIT_KHX:
> +      anv_semaphore_impl_cleanup(device, &semaphore->temporary);
> +
> +      semaphore->temporary.type = ANV_SEMAPHORE_TYPE_SYNC_FILE;
> +      semaphore->temporary.fd = pImportSemaphoreFdInfo->fd;
> +
> +      return VK_SUCCESS;
> +
>     default:
>        return vk_error(VK_ERROR_INVALID_EXTERNAL_HANDLE_KHX);
>     }
> @@ -673,6 +723,23 @@ VkResult anv_GetSemaphoreFdKHX(
>        return anv_bo_cache_export(device, &device->bo_cache,
>                                   semaphore->permanent.bo, pFd);
>  
> +   case ANV_SEMAPHORE_TYPE_SYNC_FILE:
> +      /* There are two reasons why this could happen:
> +       *
> +       *  1) The user is trying to export without submitting something that
> +       *     signals the semaphore.  If this is the case, it's their bug so
> +       *     what we return here doesn't matter.
> +       *
> +       *  2) The kernel didn't give us a file descriptor.  The most likely
> +       *     reason for this is running out of file descriptors.
> +       */
> +      if (semaphore->permanent.fd < 0)
> +         return vk_error(VK_ERROR_TOO_MANY_OBJECTS);
> +
> +      *pFd = semaphore->permanent.fd;
> +      semaphore->permanent.fd = -1;
> +      return VK_SUCCESS;
> +
>     default:
>        return vk_error(VK_ERROR_INVALID_EXTERNAL_HANDLE_KHX);
>     }
> -- 
> 2.5.0.400.gff86faf
> 
> _______________________________________________
> mesa-dev mailing list
> mesa-dev at lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/mesa-dev


More information about the mesa-dev mailing list