Home | History | Annotate | Line # | Download | only in vulkan
      1 /*
      2  * Copyright  2019 Google LLC
      3  *
      4  * Permission is hereby granted, free of charge, to any person obtaining a
      5  * copy of this software and associated documentation files (the "Software"),
      6  * to deal in the Software without restriction, including without limitation
      7  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      8  * and/or sell copies of the Software, and to permit persons to whom the
      9  * Software is furnished to do so, subject to the following conditions:
     10  *
     11  * The above copyright notice and this permission notice (including the next
     12  * paragraph) shall be included in all copies or substantial portions of the
     13  * Software.
     14  *
     15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
     18  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
     20  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
     21  * DEALINGS IN THE SOFTWARE.
     22  */
     23 
     24 #include "tu_private.h"
     25 
     26 #include <fcntl.h>
     27 #include <libsync.h>
     28 #include <unistd.h>
     29 
     30 #include "util/os_time.h"
     31 
     32 /**
     33  * Internally, a fence can be in one of these states.
     34  */
     35 enum tu_fence_state
     36 {
     37    TU_FENCE_STATE_RESET,
     38    TU_FENCE_STATE_PENDING,
     39    TU_FENCE_STATE_SIGNALED,
     40 };
     41 
     42 static enum tu_fence_state
     43 tu_fence_get_state(const struct tu_fence *fence)
     44 {
     45    if (fence->signaled)
     46       assert(fence->fd < 0);
     47 
     48    if (fence->signaled)
     49       return TU_FENCE_STATE_SIGNALED;
     50    else if (fence->fd >= 0)
     51       return TU_FENCE_STATE_PENDING;
     52    else
     53       return TU_FENCE_STATE_RESET;
     54 }
     55 
     56 static void
     57 tu_fence_set_state(struct tu_fence *fence, enum tu_fence_state state, int fd)
     58 {
     59    if (fence->fd >= 0)
     60       close(fence->fd);
     61 
     62    switch (state) {
     63    case TU_FENCE_STATE_RESET:
     64       assert(fd < 0);
     65       fence->signaled = false;
     66       fence->fd = -1;
     67       break;
     68    case TU_FENCE_STATE_PENDING:
     69       assert(fd >= 0);
     70       fence->signaled = false;
     71       fence->fd = fd;
     72       break;
     73    case TU_FENCE_STATE_SIGNALED:
     74       assert(fd < 0);
     75       fence->signaled = true;
     76       fence->fd = -1;
     77       break;
     78    default:
     79       unreachable("unknown fence state");
     80       break;
     81    }
     82 }
     83 
     84 void
     85 tu_fence_init(struct tu_fence *fence, bool signaled)
     86 {
     87    fence->signaled = signaled;
     88    fence->fd = -1;
     89 }
     90 
     91 void
     92 tu_fence_finish(struct tu_fence *fence)
     93 {
     94    if (fence->fd >= 0)
     95       close(fence->fd);
     96 }
     97 
     98 /**
     99  * Update the associated fd of a fence.  Ownership of \a fd is transferred to
    100  * \a fence.
    101  *
    102  * This function does not block.  \a fence can also be in any state when this
    103  * function is called.  To be able to do that, the caller must make sure that,
    104  * when both the currently associated fd and the new fd are valid, they are on
    105  * the same timeline with the new fd being later on the timeline.
    106  */
    107 void
    108 tu_fence_update_fd(struct tu_fence *fence, int fd)
    109 {
    110    const enum tu_fence_state state =
    111       fd >= 0 ? TU_FENCE_STATE_PENDING : TU_FENCE_STATE_SIGNALED;
    112    tu_fence_set_state(fence, state, fd);
    113 }
    114 
    115 /**
    116  * Make a fence a copy of another fence.  \a fence must be in the reset state.
    117  */
    118 void
    119 tu_fence_copy(struct tu_fence *fence, const struct tu_fence *src)
    120 {
    121    assert(tu_fence_get_state(fence) == TU_FENCE_STATE_RESET);
    122 
    123    /* dup src->fd */
    124    int fd = -1;
    125    if (src->fd >= 0) {
    126       fd = fcntl(src->fd, F_DUPFD_CLOEXEC, 0);
    127       if (fd < 0) {
    128          tu_loge("failed to dup fd %d for fence", src->fd);
    129          sync_wait(src->fd, -1);
    130       }
    131    }
    132 
    133    tu_fence_update_fd(fence, fd);
    134 }
    135 
    136 /**
    137  * Signal a fence.  \a fence must be in the reset state.
    138  */
    139 void
    140 tu_fence_signal(struct tu_fence *fence)
    141 {
    142    assert(tu_fence_get_state(fence) == TU_FENCE_STATE_RESET);
    143    tu_fence_set_state(fence, TU_FENCE_STATE_SIGNALED, -1);
    144 }
    145 
    146 /**
    147  * Wait until a fence is idle (i.e., not pending).
    148  */
    149 void
    150 tu_fence_wait_idle(struct tu_fence *fence)
    151 {
    152    if (fence->fd >= 0) {
    153       if (sync_wait(fence->fd, -1))
    154          tu_loge("sync_wait on fence fd %d failed", fence->fd);
    155 
    156       tu_fence_set_state(fence, TU_FENCE_STATE_SIGNALED, -1);
    157    }
    158 }
    159 
    160 VkResult
    161 tu_CreateFence(VkDevice _device,
    162                const VkFenceCreateInfo *pCreateInfo,
    163                const VkAllocationCallbacks *pAllocator,
    164                VkFence *pFence)
    165 {
    166    TU_FROM_HANDLE(tu_device, device, _device);
    167 
    168    struct tu_fence *fence =
    169       vk_alloc2(&device->alloc, pAllocator, sizeof(*fence), 8,
    170                 VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
    171 
    172    if (!fence)
    173       return vk_error(device->instance, VK_ERROR_OUT_OF_HOST_MEMORY);
    174 
    175    tu_fence_init(fence, pCreateInfo->flags & VK_FENCE_CREATE_SIGNALED_BIT);
    176 
    177    *pFence = tu_fence_to_handle(fence);
    178 
    179    return VK_SUCCESS;
    180 }
    181 
    182 void
    183 tu_DestroyFence(VkDevice _device,
    184                 VkFence _fence,
    185                 const VkAllocationCallbacks *pAllocator)
    186 {
    187    TU_FROM_HANDLE(tu_device, device, _device);
    188    TU_FROM_HANDLE(tu_fence, fence, _fence);
    189 
    190    if (!fence)
    191       return;
    192 
    193    tu_fence_finish(fence);
    194 
    195    vk_free2(&device->alloc, pAllocator, fence);
    196 }
    197 
    198 /**
    199  * Initialize a pollfd array from fences.
    200  */
    201 static nfds_t
    202 tu_fence_init_poll_fds(uint32_t fence_count,
    203                        const VkFence *fences,
    204                        bool wait_all,
    205                        struct pollfd *fds)
    206 {
    207    nfds_t nfds = 0;
    208    for (uint32_t i = 0; i < fence_count; i++) {
    209       TU_FROM_HANDLE(tu_fence, fence, fences[i]);
    210 
    211       if (fence->signaled) {
    212          if (wait_all) {
    213             /* skip signaled fences */
    214             continue;
    215          } else {
    216             /* no need to poll any fd */
    217             nfds = 0;
    218             break;
    219          }
    220       }
    221 
    222       /* negative fds are never ready, which is the desired behavior */
    223       fds[nfds].fd = fence->fd;
    224       fds[nfds].events = POLLIN;
    225       fds[nfds].revents = 0;
    226       nfds++;
    227    }
    228 
    229    return nfds;
    230 }
    231 
    232 /**
    233  * Translate timeout from nanoseconds to milliseconds for poll().
    234  */
    235 static int
    236 tu_fence_get_poll_timeout(uint64_t timeout_ns)
    237 {
    238    const uint64_t ns_per_ms = 1000 * 1000;
    239    uint64_t timeout_ms = timeout_ns / ns_per_ms;
    240 
    241    /* round up if needed */
    242    if (timeout_ns - timeout_ms * ns_per_ms >= ns_per_ms / 2)
    243       timeout_ms++;
    244 
    245    return timeout_ms < INT_MAX ? timeout_ms : INT_MAX;
    246 }
    247 
    248 /**
    249  * Poll a pollfd array.
    250  */
    251 static VkResult
    252 tu_fence_poll_fds(struct pollfd *fds, nfds_t nfds, uint64_t *timeout_ns)
    253 {
    254    while (true) {
    255       /* poll */
    256       uint64_t duration = os_time_get_nano();
    257       int ret = poll(fds, nfds, tu_fence_get_poll_timeout(*timeout_ns));
    258       duration = os_time_get_nano() - duration;
    259 
    260       /* update timeout_ns */
    261       if (*timeout_ns > duration)
    262          *timeout_ns -= duration;
    263       else
    264          *timeout_ns = 0;
    265 
    266       if (ret > 0) {
    267          return VK_SUCCESS;
    268       } else if (ret == 0) {
    269          if (!*timeout_ns)
    270             return VK_TIMEOUT;
    271       } else if (errno != EINTR && errno != EAGAIN) {
    272          return VK_ERROR_OUT_OF_HOST_MEMORY;
    273       }
    274    }
    275 }
    276 
    277 /**
    278  * Update a pollfd array and the fence states.  This should be called after a
    279  * successful call to tu_fence_poll_fds.
    280  */
    281 static nfds_t
    282 tu_fence_update_fences_and_poll_fds(uint32_t fence_count,
    283                                     const VkFence *fences,
    284                                     bool wait_all,
    285                                     struct pollfd *fds)
    286 {
    287    uint32_t nfds = 0;
    288    uint32_t fds_idx = 0;
    289    for (uint32_t i = 0; i < fence_count; i++) {
    290       TU_FROM_HANDLE(tu_fence, fence, fences[i]);
    291 
    292       /* no signaled fence in fds */
    293       if (fence->signaled)
    294          continue;
    295 
    296       /* fds[fds_idx] corresponds to fences[i] */
    297       assert(fence->fd == fds[fds_idx].fd);
    298 
    299       assert(nfds <= fds_idx && fds_idx <= i);
    300 
    301       /* fd is ready (errors are treated as ready) */
    302       if (fds[fds_idx].revents) {
    303          tu_fence_set_state(fence, TU_FENCE_STATE_SIGNALED, -1);
    304       } else if (wait_all) {
    305          /* add to fds again for another poll */
    306          fds[nfds].fd = fence->fd;
    307          fds[nfds].events = POLLIN;
    308          fds[nfds].revents = 0;
    309          nfds++;
    310       }
    311 
    312       fds_idx++;
    313    }
    314 
    315    return nfds;
    316 }
    317 
    318 VkResult
    319 tu_WaitForFences(VkDevice _device,
    320                  uint32_t fenceCount,
    321                  const VkFence *pFences,
    322                  VkBool32 waitAll,
    323                  uint64_t timeout)
    324 {
    325    TU_FROM_HANDLE(tu_device, device, _device);
    326 
    327    /* add a simpler path for when fenceCount == 1? */
    328 
    329    struct pollfd stack_fds[8];
    330    struct pollfd *fds = stack_fds;
    331    if (fenceCount > ARRAY_SIZE(stack_fds)) {
    332       fds = vk_alloc(&device->alloc, sizeof(*fds) * fenceCount, 8,
    333                      VK_SYSTEM_ALLOCATION_SCOPE_COMMAND);
    334       if (!fds)
    335          return VK_ERROR_OUT_OF_HOST_MEMORY;
    336    }
    337 
    338    /* set up pollfd array and start polling */
    339    nfds_t nfds = tu_fence_init_poll_fds(fenceCount, pFences, waitAll, fds);
    340    VkResult result = VK_SUCCESS;
    341    while (nfds) {
    342       result = tu_fence_poll_fds(fds, nfds, &timeout);
    343       if (result != VK_SUCCESS)
    344          break;
    345       nfds = tu_fence_update_fences_and_poll_fds(fenceCount, pFences, waitAll,
    346                                                  fds);
    347    }
    348 
    349    if (fds != stack_fds)
    350       vk_free(&device->alloc, fds);
    351 
    352    return result;
    353 }
    354 
    355 VkResult
    356 tu_ResetFences(VkDevice _device, uint32_t fenceCount, const VkFence *pFences)
    357 {
    358    for (unsigned i = 0; i < fenceCount; ++i) {
    359       TU_FROM_HANDLE(tu_fence, fence, pFences[i]);
    360       assert(tu_fence_get_state(fence) != TU_FENCE_STATE_PENDING);
    361       tu_fence_set_state(fence, TU_FENCE_STATE_RESET, -1);
    362    }
    363 
    364    return VK_SUCCESS;
    365 }
    366 
    367 VkResult
    368 tu_GetFenceStatus(VkDevice _device, VkFence _fence)
    369 {
    370    TU_FROM_HANDLE(tu_fence, fence, _fence);
    371 
    372    if (fence->fd >= 0) {
    373       int err = sync_wait(fence->fd, 0);
    374       if (!err)
    375          tu_fence_set_state(fence, TU_FENCE_STATE_SIGNALED, -1);
    376       else if (err && errno != ETIME)
    377          return VK_ERROR_OUT_OF_HOST_MEMORY;
    378    }
    379 
    380    return fence->signaled ? VK_SUCCESS : VK_NOT_READY;
    381 }
    382