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 */ 35enum tu_fence_state 36{ 37 TU_FENCE_STATE_RESET, 38 TU_FENCE_STATE_PENDING, 39 TU_FENCE_STATE_SIGNALED, 40}; 41 42static enum tu_fence_state 43tu_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 56static void 57tu_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 84void 85tu_fence_init(struct tu_fence *fence, bool signaled) 86{ 87 fence->signaled = signaled; 88 fence->fd = -1; 89} 90 91void 92tu_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 */ 107void 108tu_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 */ 118void 119tu_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 */ 139void 140tu_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 */ 149void 150tu_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 160VkResult 161tu_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 182void 183tu_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 */ 201static nfds_t 202tu_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 */ 235static int 236tu_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 */ 251static VkResult 252tu_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 */ 281static nfds_t 282tu_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 318VkResult 319tu_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 355VkResult 356tu_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 367VkResult 368tu_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