1/* 2 * Copyright © 2021 Intel Corporation 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 DEALINGS 21 * IN THE SOFTWARE. 22 */ 23 24#include "vk_fence.h" 25 26#include "util/os_time.h" 27 28#ifndef _WIN32 29#include <unistd.h> 30#endif 31 32#include "vk_common_entrypoints.h" 33#include "vk_device.h" 34#include "vk_log.h" 35#include "vk_physical_device.h" 36#include "vk_util.h" 37 38static VkExternalFenceHandleTypeFlags 39vk_sync_fence_import_types(const struct vk_sync_type *type) 40{ 41 VkExternalFenceHandleTypeFlags handle_types = 0; 42 43 if (type->import_opaque_fd) 44 handle_types |= VK_EXTERNAL_FENCE_HANDLE_TYPE_OPAQUE_FD_BIT; 45 46 if (type->import_sync_file) 47 handle_types |= VK_EXTERNAL_FENCE_HANDLE_TYPE_SYNC_FD_BIT; 48 49 return handle_types; 50} 51 52static VkExternalFenceHandleTypeFlags 53vk_sync_fence_export_types(const struct vk_sync_type *type) 54{ 55 VkExternalFenceHandleTypeFlags handle_types = 0; 56 57 if (type->export_opaque_fd) 58 handle_types |= VK_EXTERNAL_FENCE_HANDLE_TYPE_OPAQUE_FD_BIT; 59 60 if (type->export_sync_file) 61 handle_types |= VK_EXTERNAL_FENCE_HANDLE_TYPE_SYNC_FD_BIT; 62 63 return handle_types; 64} 65 66static VkExternalFenceHandleTypeFlags 67vk_sync_fence_handle_types(const struct vk_sync_type *type) 68{ 69 return vk_sync_fence_export_types(type) & 70 vk_sync_fence_import_types(type); 71} 72 73static const struct vk_sync_type * 74get_fence_sync_type(struct vk_physical_device *pdevice, 75 VkExternalFenceHandleTypeFlags handle_types) 76{ 77 static const enum vk_sync_features req_features = 78 VK_SYNC_FEATURE_BINARY | 79 VK_SYNC_FEATURE_CPU_WAIT | 80 VK_SYNC_FEATURE_CPU_RESET; 81 82 for (const struct vk_sync_type *const *t = 83 pdevice->supported_sync_types; *t; t++) { 84 if (req_features & ~(*t)->features) 85 continue; 86 87 if (handle_types & ~vk_sync_fence_handle_types(*t)) 88 continue; 89 90 return *t; 91 } 92 93 return NULL; 94} 95 96VkResult 97vk_fence_create(struct vk_device *device, 98 const VkFenceCreateInfo *pCreateInfo, 99 const VkAllocationCallbacks *pAllocator, 100 struct vk_fence **fence_out) 101{ 102 struct vk_fence *fence; 103 104 assert(pCreateInfo->sType == VK_STRUCTURE_TYPE_FENCE_CREATE_INFO); 105 106 const VkExportFenceCreateInfo *export = 107 vk_find_struct_const(pCreateInfo->pNext, EXPORT_FENCE_CREATE_INFO); 108 VkExternalFenceHandleTypeFlags handle_types = 109 export ? export->handleTypes : 0; 110 111 const struct vk_sync_type *sync_type = 112 get_fence_sync_type(device->physical, handle_types); 113 if (sync_type == NULL) { 114 /* We should always be able to get a fence type for internal */ 115 assert(get_fence_sync_type(device->physical, 0) != NULL); 116 return vk_errorf(device, VK_ERROR_INVALID_EXTERNAL_HANDLE, 117 "Combination of external handle types is unsupported " 118 "for VkFence creation."); 119 } 120 121 /* Allocate a vk_fence + vk_sync implementation. Because the permanent 122 * field of vk_fence is the base field of the vk_sync implementation, we 123 * can make the 2 structures overlap. 124 */ 125 size_t size = offsetof(struct vk_fence, permanent) + sync_type->size; 126 fence = vk_object_zalloc(device, pAllocator, size, VK_OBJECT_TYPE_FENCE); 127 if (fence == NULL) 128 return vk_error(device, VK_ERROR_OUT_OF_HOST_MEMORY); 129 130 enum vk_sync_flags sync_flags = 0; 131 if (handle_types) 132 sync_flags |= VK_SYNC_IS_SHAREABLE; 133 134 bool signaled = pCreateInfo->flags & VK_FENCE_CREATE_SIGNALED_BIT; 135 VkResult result = vk_sync_init(device, &fence->permanent, 136 sync_type, sync_flags, signaled); 137 if (result != VK_SUCCESS) { 138 vk_object_free(device, pAllocator, fence); 139 return result; 140 } 141 142 *fence_out = fence; 143 144 return VK_SUCCESS; 145} 146 147VKAPI_ATTR VkResult VKAPI_CALL 148vk_common_CreateFence(VkDevice _device, 149 const VkFenceCreateInfo *pCreateInfo, 150 const VkAllocationCallbacks *pAllocator, 151 VkFence *pFence) 152{ 153 VK_FROM_HANDLE(vk_device, device, _device); 154 struct vk_fence *fence; 155 156 VkResult result = vk_fence_create(device, pCreateInfo, pAllocator, &fence); 157 if (result != VK_SUCCESS) 158 return result; 159 160 *pFence = vk_fence_to_handle(fence); 161 162 return VK_SUCCESS; 163} 164 165void 166vk_fence_reset_temporary(struct vk_device *device, 167 struct vk_fence *fence) 168{ 169 if (fence->temporary == NULL) 170 return; 171 172 vk_sync_destroy(device, fence->temporary); 173 fence->temporary = NULL; 174} 175 176void 177vk_fence_destroy(struct vk_device *device, 178 struct vk_fence *fence, 179 const VkAllocationCallbacks *pAllocator) 180{ 181 vk_fence_reset_temporary(device, fence); 182 vk_sync_finish(device, &fence->permanent); 183 184 vk_object_free(device, pAllocator, fence); 185} 186 187VKAPI_ATTR void VKAPI_CALL 188vk_common_DestroyFence(VkDevice _device, 189 VkFence _fence, 190 const VkAllocationCallbacks *pAllocator) 191{ 192 VK_FROM_HANDLE(vk_device, device, _device); 193 VK_FROM_HANDLE(vk_fence, fence, _fence); 194 195 if (fence == NULL) 196 return; 197 198 vk_fence_destroy(device, fence, pAllocator); 199} 200 201VKAPI_ATTR VkResult VKAPI_CALL 202vk_common_ResetFences(VkDevice _device, 203 uint32_t fenceCount, 204 const VkFence *pFences) 205{ 206 VK_FROM_HANDLE(vk_device, device, _device); 207 208 for (uint32_t i = 0; i < fenceCount; i++) { 209 VK_FROM_HANDLE(vk_fence, fence, pFences[i]); 210 211 /* From the Vulkan 1.2.194 spec: 212 * 213 * "If any member of pFences currently has its payload imported with 214 * temporary permanence, that fence’s prior permanent payload is 215 * first restored. The remaining operations described therefore 216 * operate on the restored payload." 217 */ 218 vk_fence_reset_temporary(device, fence); 219 220 VkResult result = vk_sync_reset(device, &fence->permanent); 221 if (result != VK_SUCCESS) 222 return result; 223 } 224 225 return VK_SUCCESS; 226} 227 228VKAPI_ATTR VkResult VKAPI_CALL 229vk_common_GetFenceStatus(VkDevice _device, 230 VkFence _fence) 231{ 232 VK_FROM_HANDLE(vk_device, device, _device); 233 VK_FROM_HANDLE(vk_fence, fence, _fence); 234 235 if (vk_device_is_lost(device)) 236 return VK_ERROR_DEVICE_LOST; 237 238 VkResult result = vk_sync_wait(device, vk_fence_get_active_sync(fence), 239 0 /* wait_value */, 240 VK_SYNC_WAIT_COMPLETE, 241 0 /* abs_timeout_ns */); 242 if (result == VK_TIMEOUT) 243 return VK_NOT_READY; 244 else 245 return result; 246} 247 248VKAPI_ATTR VkResult VKAPI_CALL 249vk_common_WaitForFences(VkDevice _device, 250 uint32_t fenceCount, 251 const VkFence *pFences, 252 VkBool32 waitAll, 253 uint64_t timeout) 254{ 255 VK_FROM_HANDLE(vk_device, device, _device); 256 257 if (vk_device_is_lost(device)) 258 return VK_ERROR_DEVICE_LOST; 259 260 if (fenceCount == 0) 261 return VK_SUCCESS; 262 263 uint64_t abs_timeout_ns = os_time_get_absolute_timeout(timeout); 264 265 STACK_ARRAY(struct vk_sync_wait, waits, fenceCount); 266 267 for (uint32_t i = 0; i < fenceCount; i++) { 268 VK_FROM_HANDLE(vk_fence, fence, pFences[i]); 269 waits[i] = (struct vk_sync_wait) { 270 .sync = vk_fence_get_active_sync(fence), 271 .stage_mask = ~(VkPipelineStageFlags2)0, 272 }; 273 } 274 275 enum vk_sync_wait_flags wait_flags = VK_SYNC_WAIT_COMPLETE; 276 if (!waitAll) 277 wait_flags |= VK_SYNC_WAIT_ANY; 278 279 VkResult result = vk_sync_wait_many(device, fenceCount, waits, 280 wait_flags, abs_timeout_ns); 281 282 STACK_ARRAY_FINISH(waits); 283 284 VkResult device_status = vk_device_check_status(device); 285 if (device_status != VK_SUCCESS) 286 return device_status; 287 288 return result; 289} 290 291VKAPI_ATTR void VKAPI_CALL 292vk_common_GetPhysicalDeviceExternalFenceProperties( 293 VkPhysicalDevice physicalDevice, 294 const VkPhysicalDeviceExternalFenceInfo *pExternalFenceInfo, 295 VkExternalFenceProperties *pExternalFenceProperties) 296{ 297 VK_FROM_HANDLE(vk_physical_device, pdevice, physicalDevice); 298 299 assert(pExternalFenceInfo->sType == 300 VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_FENCE_INFO); 301 const VkExternalFenceHandleTypeFlagBits handle_type = 302 pExternalFenceInfo->handleType; 303 304 const struct vk_sync_type *sync_type = 305 get_fence_sync_type(pdevice, handle_type); 306 if (sync_type == NULL) { 307 pExternalFenceProperties->exportFromImportedHandleTypes = 0; 308 pExternalFenceProperties->compatibleHandleTypes = 0; 309 pExternalFenceProperties->externalFenceFeatures = 0; 310 return; 311 } 312 313 VkExternalFenceHandleTypeFlagBits import = 314 vk_sync_fence_import_types(sync_type); 315 VkExternalFenceHandleTypeFlagBits export = 316 vk_sync_fence_export_types(sync_type); 317 318 if (handle_type != VK_EXTERNAL_FENCE_HANDLE_TYPE_OPAQUE_FD_BIT) { 319 const struct vk_sync_type *opaque_sync_type = 320 get_fence_sync_type(pdevice, VK_EXTERNAL_FENCE_HANDLE_TYPE_OPAQUE_FD_BIT); 321 322 /* If we're a different vk_sync_type than the one selected when only 323 * OPAQUE_FD is set, then we can't import/export OPAQUE_FD. Put 324 * differently, there can only be one OPAQUE_FD sync type. 325 */ 326 if (sync_type != opaque_sync_type) { 327 import &= ~VK_EXTERNAL_FENCE_HANDLE_TYPE_OPAQUE_FD_BIT; 328 export &= ~VK_EXTERNAL_FENCE_HANDLE_TYPE_OPAQUE_FD_BIT; 329 } 330 } 331 332 VkExternalFenceHandleTypeFlags compatible = import & export; 333 VkExternalFenceFeatureFlags features = 0; 334 if (handle_type & export) 335 features |= VK_EXTERNAL_FENCE_FEATURE_EXPORTABLE_BIT; 336 if (handle_type & import) 337 features |= VK_EXTERNAL_FENCE_FEATURE_IMPORTABLE_BIT; 338 339 pExternalFenceProperties->exportFromImportedHandleTypes = export; 340 pExternalFenceProperties->compatibleHandleTypes = compatible; 341 pExternalFenceProperties->externalFenceFeatures = features; 342} 343 344#ifndef _WIN32 345 346VKAPI_ATTR VkResult VKAPI_CALL 347vk_common_ImportFenceFdKHR(VkDevice _device, 348 const VkImportFenceFdInfoKHR *pImportFenceFdInfo) 349{ 350 VK_FROM_HANDLE(vk_device, device, _device); 351 VK_FROM_HANDLE(vk_fence, fence, pImportFenceFdInfo->fence); 352 353 assert(pImportFenceFdInfo->sType == 354 VK_STRUCTURE_TYPE_IMPORT_FENCE_FD_INFO_KHR); 355 356 const int fd = pImportFenceFdInfo->fd; 357 const VkExternalFenceHandleTypeFlagBits handle_type = 358 pImportFenceFdInfo->handleType; 359 360 struct vk_sync *temporary = NULL, *sync; 361 if (pImportFenceFdInfo->flags & VK_FENCE_IMPORT_TEMPORARY_BIT) { 362 const struct vk_sync_type *sync_type = 363 get_fence_sync_type(device->physical, handle_type); 364 365 VkResult result = vk_sync_create(device, sync_type, 0 /* flags */, 366 0 /* initial_value */, &temporary); 367 if (result != VK_SUCCESS) 368 return result; 369 370 sync = temporary; 371 } else { 372 sync = &fence->permanent; 373 } 374 assert(handle_type & vk_sync_fence_handle_types(sync->type)); 375 376 VkResult result; 377 switch (pImportFenceFdInfo->handleType) { 378 case VK_EXTERNAL_FENCE_HANDLE_TYPE_OPAQUE_FD_BIT: 379 result = vk_sync_import_opaque_fd(device, sync, fd); 380 break; 381 382 case VK_EXTERNAL_FENCE_HANDLE_TYPE_SYNC_FD_BIT: 383 result = vk_sync_import_sync_file(device, sync, fd); 384 break; 385 386 default: 387 result = vk_error(fence, VK_ERROR_INVALID_EXTERNAL_HANDLE); 388 } 389 390 if (result != VK_SUCCESS) { 391 if (temporary != NULL) 392 vk_sync_destroy(device, temporary); 393 return result; 394 } 395 396 /* From the Vulkan 1.2.194 spec: 397 * 398 * "Importing a fence payload from a file descriptor transfers 399 * ownership of the file descriptor from the application to the 400 * Vulkan implementation. The application must not perform any 401 * operations on the file descriptor after a successful import." 402 * 403 * If the import fails, we leave the file descriptor open. 404 */ 405 if (fd != -1) 406 close(fd); 407 408 if (temporary) { 409 vk_fence_reset_temporary(device, fence); 410 fence->temporary = temporary; 411 } 412 413 return VK_SUCCESS; 414} 415 416VKAPI_ATTR VkResult VKAPI_CALL 417vk_common_GetFenceFdKHR(VkDevice _device, 418 const VkFenceGetFdInfoKHR *pGetFdInfo, 419 int *pFd) 420{ 421 VK_FROM_HANDLE(vk_device, device, _device); 422 VK_FROM_HANDLE(vk_fence, fence, pGetFdInfo->fence); 423 424 assert(pGetFdInfo->sType == VK_STRUCTURE_TYPE_FENCE_GET_FD_INFO_KHR); 425 426 struct vk_sync *sync = vk_fence_get_active_sync(fence); 427 428 VkResult result; 429 switch (pGetFdInfo->handleType) { 430 case VK_EXTERNAL_FENCE_HANDLE_TYPE_OPAQUE_FD_BIT: 431 result = vk_sync_export_opaque_fd(device, sync, pFd); 432 if (unlikely(result != VK_SUCCESS)) 433 return result; 434 break; 435 436 case VK_EXTERNAL_FENCE_HANDLE_TYPE_SYNC_FD_BIT: 437 /* There's no direct spec quote for this but the same rules as for 438 * semaphore export apply. We can't export a sync file from a fence 439 * if the fence event hasn't been submitted to the kernel yet. 440 */ 441 if (vk_device_supports_threaded_submit(device)) { 442 result = vk_sync_wait(device, sync, 0, 443 VK_SYNC_WAIT_PENDING, 444 UINT64_MAX); 445 if (unlikely(result != VK_SUCCESS)) 446 return result; 447 } 448 449 result = vk_sync_export_sync_file(device, sync, pFd); 450 if (unlikely(result != VK_SUCCESS)) 451 return result; 452 453 /* From the Vulkan 1.2.194 spec: 454 * 455 * "Export operations have the same transference as the specified 456 * handle type’s import operations. Additionally, exporting a fence 457 * payload to a handle with copy transference has the same side 458 * effects on the source fence’s payload as executing a fence reset 459 * operation." 460 * 461 * In other words, exporting a sync file also resets the fence. We 462 * only care about this for the permanent payload because the temporary 463 * payload will be destroyed below. 464 */ 465 if (sync == &fence->permanent) { 466 result = vk_sync_reset(device, sync); 467 if (unlikely(result != VK_SUCCESS)) 468 return result; 469 } 470 break; 471 472 default: 473 unreachable("Invalid fence export handle type"); 474 } 475 476 /* From the Vulkan 1.2.194 spec: 477 * 478 * "Export operations have the same transference as the specified 479 * handle type’s import operations. [...] If the fence was using a 480 * temporarily imported payload, the fence’s prior permanent payload 481 * will be restored. 482 */ 483 vk_fence_reset_temporary(device, fence); 484 485 return VK_SUCCESS; 486} 487 488#endif /* !defined(_WIN32) */ 489