Edit

IABSD.fr/xenocara/lib/mesa/src/vulkan/runtime/vk_drm_syncobj.c

Branch :

  • Show log

    Commit

  • Author : jsg
    Date : 2022-09-02 05:12:48
    Hash : 4968d392
    Message : Import Mesa 22.1.7

  • lib/mesa/src/vulkan/runtime/vk_drm_syncobj.c
  • /*
     * Copyright © 2021 Intel Corporation
     *
     * Permission is hereby granted, free of charge, to any person obtaining a
     * copy of this software and associated documentation files (the "Software"),
     * to deal in the Software without restriction, including without limitation
     * the rights to use, copy, modify, merge, publish, distribute, sublicense,
     * and/or sell copies of the Software, and to permit persons to whom the
     * Software is furnished to do so, subject to the following conditions:
     *
     * The above copyright notice and this permission notice (including the next
     * paragraph) shall be included in all copies or substantial portions of the
     * Software.
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
     * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
     * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
     * IN THE SOFTWARE.
     */
    
    #include "vk_drm_syncobj.h"
    
    #include <sched.h>
    #include <xf86drm.h>
    
    #include "drm-uapi/drm.h"
    
    #include "util/os_time.h"
    
    #include "vk_device.h"
    #include "vk_log.h"
    #include "vk_util.h"
    
    static struct vk_drm_syncobj *
    to_drm_syncobj(struct vk_sync *sync)
    {
       assert(vk_sync_type_is_drm_syncobj(sync->type));
       return container_of(sync, struct vk_drm_syncobj, base);
    }
    
    static VkResult
    vk_drm_syncobj_init(struct vk_device *device,
                        struct vk_sync *sync,
                        uint64_t initial_value)
    {
       struct vk_drm_syncobj *sobj = to_drm_syncobj(sync);
    
       uint32_t flags = 0;
       if (!(sync->flags & VK_SYNC_IS_TIMELINE) && initial_value)
          flags |= DRM_SYNCOBJ_CREATE_SIGNALED;
    
       assert(device->drm_fd >= 0);
       int err = drmSyncobjCreate(device->drm_fd, flags, &sobj->syncobj);
       if (err < 0) {
          return vk_errorf(device, VK_ERROR_OUT_OF_HOST_MEMORY,
                           "DRM_IOCTL_SYNCOBJ_CREATE failed: %m");
       }
    
       if ((sync->flags & VK_SYNC_IS_TIMELINE) && initial_value) {
          err = drmSyncobjTimelineSignal(device->drm_fd, &sobj->syncobj,
                                         &initial_value, 1);
          if (err < 0) {
             vk_drm_syncobj_finish(device, sync);
             return vk_errorf(device, VK_ERROR_OUT_OF_HOST_MEMORY,
                              "DRM_IOCTL_SYNCOBJ_CREATE failed: %m");
          }
       }
    
       return VK_SUCCESS;
    }
    
    void
    vk_drm_syncobj_finish(struct vk_device *device,
                          struct vk_sync *sync)
    {
       struct vk_drm_syncobj *sobj = to_drm_syncobj(sync);
    
       assert(device->drm_fd >= 0);
       ASSERTED int err = drmSyncobjDestroy(device->drm_fd, sobj->syncobj);
       assert(err == 0);
    }
    
    static VkResult
    vk_drm_syncobj_signal(struct vk_device *device,
                          struct vk_sync *sync,
                          uint64_t value)
    {
       struct vk_drm_syncobj *sobj = to_drm_syncobj(sync);
    
       assert(device->drm_fd >= 0);
       int err;
       if (sync->flags & VK_SYNC_IS_TIMELINE)
          err = drmSyncobjTimelineSignal(device->drm_fd, &sobj->syncobj, &value, 1);
       else
          err = drmSyncobjSignal(device->drm_fd, &sobj->syncobj, 1);
       if (err) {
          return vk_errorf(device, VK_ERROR_UNKNOWN,
                           "DRM_IOCTL_SYNCOBJ_SIGNAL failed: %m");
       }
    
       return VK_SUCCESS;
    }
    
    static VkResult
    vk_drm_syncobj_get_value(struct vk_device *device,
                             struct vk_sync *sync,
                             uint64_t *value)
    {
       struct vk_drm_syncobj *sobj = to_drm_syncobj(sync);
    
       assert(device->drm_fd >= 0);
       int err = drmSyncobjQuery(device->drm_fd, &sobj->syncobj, value, 1);
       if (err) {
          return vk_errorf(device, VK_ERROR_UNKNOWN,
                           "DRM_IOCTL_SYNCOBJ_QUERY failed: %m");
       }
    
       return VK_SUCCESS;
    }
    
    static VkResult
    vk_drm_syncobj_reset(struct vk_device *device,
                         struct vk_sync *sync)
    {
       struct vk_drm_syncobj *sobj = to_drm_syncobj(sync);
    
       assert(device->drm_fd >= 0);
       int err = drmSyncobjReset(device->drm_fd, &sobj->syncobj, 1);
       if (err) {
          return vk_errorf(device, VK_ERROR_UNKNOWN,
                           "DRM_IOCTL_SYNCOBJ_RESET failed: %m");
       }
    
       return VK_SUCCESS;
    }
    
    static VkResult
    sync_has_sync_file(struct vk_device *device, struct vk_sync *sync)
    {
       uint32_t handle = to_drm_syncobj(sync)->syncobj;
    
       int fd = -1;
       int err = drmSyncobjExportSyncFile(device->drm_fd, handle, &fd);
       if (!err) {
          close(fd);
          return VK_SUCCESS;
       }
    
       /* On the off chance the sync_file export repeatedly fails for some
        * unexpected reason, we want to ensure this function will return success
        * eventually.  Do a zero-time syncobj wait if the export failed.
        */
       err = drmSyncobjWait(device->drm_fd, &handle, 1, 0 /* timeout */,
                            DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT,
                            NULL /* first_signaled */);
       if (!err) {
          return VK_SUCCESS;
       } else if (errno == ETIME) {
          return VK_TIMEOUT;
       } else {
          return vk_errorf(device, VK_ERROR_UNKNOWN,
                           "DRM_IOCTL_SYNCOBJ_WAIT failed: %m");
       }
    }
    
    static VkResult
    spin_wait_for_sync_file(struct vk_device *device,
                            uint32_t wait_count,
                            const struct vk_sync_wait *waits,
                            enum vk_sync_wait_flags wait_flags,
                            uint64_t abs_timeout_ns)
    {
       if (wait_flags & VK_SYNC_WAIT_ANY) {
          while (1) {
             for (uint32_t i = 0; i < wait_count; i++) {
                VkResult result = sync_has_sync_file(device, waits[i].sync);
                if (result != VK_TIMEOUT)
                   return result;
             }
    
             if (os_time_get_nano() >= abs_timeout_ns)
                return VK_TIMEOUT;
    
             sched_yield();
          }
       } else {
          for (uint32_t i = 0; i < wait_count; i++) {
             while (1) {
                VkResult result = sync_has_sync_file(device, waits[i].sync);
                if (result != VK_TIMEOUT)
                   return result;
    
                if (os_time_get_nano() >= abs_timeout_ns)
                   return VK_TIMEOUT;
    
                sched_yield();
             }
          }
       }
    
       return VK_SUCCESS;
    }
    
    static VkResult
    vk_drm_syncobj_wait_many(struct vk_device *device,
                             uint32_t wait_count,
                             const struct vk_sync_wait *waits,
                             enum vk_sync_wait_flags wait_flags,
                             uint64_t abs_timeout_ns)
    {
       if ((wait_flags & VK_SYNC_WAIT_PENDING) &&
           !(waits[0].sync->type->features & VK_SYNC_FEATURE_TIMELINE)) {
          /* Sadly, DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE was never implemented
           * for drivers that don't support timelines.  Instead, we have to spin
           * on DRM_IOCTL_SYNCOBJ_FD_TO_HANDLE until it succeeds.
           */
          return spin_wait_for_sync_file(device, wait_count, waits,
                                         wait_flags, abs_timeout_ns);
       }
    
       /* Syncobj timeouts are signed */
       abs_timeout_ns = MIN2(abs_timeout_ns, (uint64_t)INT64_MAX);
    
       STACK_ARRAY(uint32_t, handles, wait_count);
       STACK_ARRAY(uint64_t, wait_values, wait_count);
    
       uint32_t j = 0;
       bool has_timeline = false;
       for (uint32_t i = 0; i < wait_count; i++) {
          /* The syncobj API doesn't like wait values of 0 but it's safe to skip
           * them because a wait for 0 is a no-op.
           */
          if (waits[i].sync->flags & VK_SYNC_IS_TIMELINE) {
             if (waits[i].wait_value == 0)
                continue;
    
             has_timeline = true;
          }
    
          handles[j] = to_drm_syncobj(waits[i].sync)->syncobj;
          wait_values[j] = waits[i].wait_value;
          j++;
       }
       assert(j <= wait_count);
       wait_count = j;
    
       uint32_t syncobj_wait_flags = DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT;
       if (!(wait_flags & VK_SYNC_WAIT_ANY))
          syncobj_wait_flags |= DRM_SYNCOBJ_WAIT_FLAGS_WAIT_ALL;
    
       assert(device->drm_fd >= 0);
       int err;
       if (wait_count == 0) {
          err = 0;
       } else if (wait_flags & VK_SYNC_WAIT_PENDING) {
          /* We always use a timeline wait for WAIT_PENDING, even for binary
           * syncobjs because the non-timeline wait doesn't support
           * DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE.
           */
          err = drmSyncobjTimelineWait(device->drm_fd, handles, wait_values,
                                       wait_count, abs_timeout_ns,
                                       syncobj_wait_flags |
                                       DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE,
                                       NULL /* first_signaled */);
       } else if (has_timeline) {
          err = drmSyncobjTimelineWait(device->drm_fd, handles, wait_values,
                                       wait_count, abs_timeout_ns,
                                       syncobj_wait_flags,
                                       NULL /* first_signaled */);
       } else {
          err = drmSyncobjWait(device->drm_fd, handles,
                               wait_count, abs_timeout_ns,
                               syncobj_wait_flags,
                               NULL /* first_signaled */);
       }
    
       STACK_ARRAY_FINISH(handles);
       STACK_ARRAY_FINISH(wait_values);
    
       if (err && errno == ETIME) {
          return VK_TIMEOUT;
       } else if (err) {
          return vk_errorf(device, VK_ERROR_UNKNOWN,
                           "DRM_IOCTL_SYNCOBJ_WAIT failed: %m");
       }
    
       return VK_SUCCESS;
    }
    
    static VkResult
    vk_drm_syncobj_import_opaque_fd(struct vk_device *device,
                                    struct vk_sync *sync,
                                    int fd)
    {
       struct vk_drm_syncobj *sobj = to_drm_syncobj(sync);
    
       assert(device->drm_fd >= 0);
       uint32_t new_handle;
       int err = drmSyncobjFDToHandle(device->drm_fd, fd, &new_handle);
       if (err) {
          return vk_errorf(device, VK_ERROR_UNKNOWN,
                           "DRM_IOCTL_SYNCOBJ_FD_TO_HANDLE failed: %m");
       }
    
       err = drmSyncobjDestroy(device->drm_fd, sobj->syncobj);
       assert(!err);
    
       sobj->syncobj = new_handle;
    
       return VK_SUCCESS;
    }
    
    static VkResult
    vk_drm_syncobj_export_opaque_fd(struct vk_device *device,
                                    struct vk_sync *sync,
                                    int *fd)
    {
       struct vk_drm_syncobj *sobj = to_drm_syncobj(sync);
    
       assert(device->drm_fd >= 0);
       int err = drmSyncobjHandleToFD(device->drm_fd, sobj->syncobj, fd);
       if (err) {
          return vk_errorf(device, VK_ERROR_UNKNOWN,
                           "DRM_IOCTL_SYNCOBJ_HANDLE_TO_FD failed: %m");
       }
    
       return VK_SUCCESS;
    }
    
    static VkResult
    vk_drm_syncobj_import_sync_file(struct vk_device *device,
                                    struct vk_sync *sync,
                                    int sync_file)
    {
       struct vk_drm_syncobj *sobj = to_drm_syncobj(sync);
    
       assert(device->drm_fd >= 0);
       int err = drmSyncobjImportSyncFile(device->drm_fd, sobj->syncobj, sync_file);
       if (err) {
          return vk_errorf(device, VK_ERROR_UNKNOWN,
                           "DRM_IOCTL_SYNCOBJ_FD_TO_HANDLE failed: %m");
       }
    
       return VK_SUCCESS;
    }
    
    static VkResult
    vk_drm_syncobj_export_sync_file(struct vk_device *device,
                                    struct vk_sync *sync,
                                    int *sync_file)
    {
       struct vk_drm_syncobj *sobj = to_drm_syncobj(sync);
    
       assert(device->drm_fd >= 0);
       int err = drmSyncobjExportSyncFile(device->drm_fd, sobj->syncobj, sync_file);
       if (err) {
          return vk_errorf(device, VK_ERROR_UNKNOWN,
                           "DRM_IOCTL_SYNCOBJ_HANDLE_TO_FD failed: %m");
       }
    
       return VK_SUCCESS;
    }
    
    static VkResult
    vk_drm_syncobj_move(struct vk_device *device,
                        struct vk_sync *dst,
                        struct vk_sync *src)
    {
       struct vk_drm_syncobj *dst_sobj = to_drm_syncobj(dst);
       struct vk_drm_syncobj *src_sobj = to_drm_syncobj(src);
       VkResult result;
    
       if (!(dst->flags & VK_SYNC_IS_SHARED) &&
           !(src->flags & VK_SYNC_IS_SHARED)) {
          result = vk_drm_syncobj_reset(device, dst);
          if (unlikely(result != VK_SUCCESS))
             return result;
    
          uint32_t tmp = dst_sobj->syncobj;
          dst_sobj->syncobj = src_sobj->syncobj;
          src_sobj->syncobj = tmp;
    
          return VK_SUCCESS;
       } else {
          int fd;
          result = vk_drm_syncobj_export_sync_file(device, src, &fd);
          if (result != VK_SUCCESS)
             return result;
    
          result = vk_drm_syncobj_import_sync_file(device, dst, fd);
          if (fd >= 0)
             close(fd);
          if (result != VK_SUCCESS)
             return result;
    
          return vk_drm_syncobj_reset(device, src);
       }
    }
    
    struct vk_sync_type
    vk_drm_syncobj_get_type(int drm_fd)
    {
       uint32_t syncobj = 0;
       int err = drmSyncobjCreate(drm_fd, DRM_SYNCOBJ_CREATE_SIGNALED, &syncobj);
       if (err < 0)
          return (struct vk_sync_type) { .features = 0 };
    
       struct vk_sync_type type = {
          .size = sizeof(struct vk_drm_syncobj),
          .features = VK_SYNC_FEATURE_BINARY |
                      VK_SYNC_FEATURE_GPU_WAIT |
                      VK_SYNC_FEATURE_CPU_RESET |
                      VK_SYNC_FEATURE_CPU_SIGNAL |
                      VK_SYNC_FEATURE_WAIT_PENDING,
          .init = vk_drm_syncobj_init,
          .finish = vk_drm_syncobj_finish,
          .signal = vk_drm_syncobj_signal,
          .reset = vk_drm_syncobj_reset,
          .move = vk_drm_syncobj_move,
          .import_opaque_fd = vk_drm_syncobj_import_opaque_fd,
          .export_opaque_fd = vk_drm_syncobj_export_opaque_fd,
          .import_sync_file = vk_drm_syncobj_import_sync_file,
          .export_sync_file = vk_drm_syncobj_export_sync_file,
       };
    
       err = drmSyncobjWait(drm_fd, &syncobj, 1, 0,
                            DRM_SYNCOBJ_WAIT_FLAGS_WAIT_ALL,
                            NULL /* first_signaled */);
       if (err == 0) {
          type.wait_many = vk_drm_syncobj_wait_many;
          type.features |= VK_SYNC_FEATURE_CPU_WAIT |
                           VK_SYNC_FEATURE_WAIT_ANY;
       }
    
       uint64_t cap;
       err = drmGetCap(drm_fd, DRM_CAP_SYNCOBJ_TIMELINE, &cap);
       if (err == 0 && cap != 0) {
          type.get_value = vk_drm_syncobj_get_value;
          type.features |= VK_SYNC_FEATURE_TIMELINE;
       }
    
       err = drmSyncobjDestroy(drm_fd, syncobj);
       assert(err == 0);
    
       return type;
    }