/* * Copyright © 2020 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_device.h" #include "vk_alloc.h" #include "vk_common_entrypoints.h" #include "vk_fence.h" #include "vk_instance.h" #include "vk_log.h" #include "vk_physical_device.h" #include "vk_queue.h" #include "vk_semaphore.h" #include "vk_sync.h" #include "vk_sync_timeline.h" #include "vk_util.h" #include "util/compiler.h" #include "util/u_debug.h" #include "util/hash_table.h" #include "util/perf/cpu_trace.h" #include "util/ralloc.h" #include "util/timespec.h" static enum vk_device_timeline_mode get_timeline_mode(struct vk_physical_device *physical_device) { if (physical_device->supported_sync_types == NULL) return VK_DEVICE_TIMELINE_MODE_NONE; const struct vk_sync_type *timeline_type = NULL; for (const struct vk_sync_type *const *t = physical_device->supported_sync_types; *t; t++) { if ((*t)->features & VK_SYNC_FEATURE_TIMELINE) { /* We can only have one timeline mode */ assert(timeline_type == NULL); timeline_type = *t; } } if (timeline_type == NULL) return VK_DEVICE_TIMELINE_MODE_NONE; if (vk_sync_type_is_vk_sync_timeline(timeline_type)) return VK_DEVICE_TIMELINE_MODE_EMULATED; if (timeline_type->features & VK_SYNC_FEATURE_WAIT_BEFORE_SIGNAL) return VK_DEVICE_TIMELINE_MODE_NATIVE; /* For assisted mode, we require a few additional things of all sync types * which may be used as semaphores. */ for (const struct vk_sync_type *const *t = physical_device->supported_sync_types; *t; t++) { if ((*t)->features & VK_SYNC_FEATURE_GPU_WAIT) { assert((*t)->features & VK_SYNC_FEATURE_WAIT_PENDING); if ((*t)->features & VK_SYNC_FEATURE_BINARY) assert((*t)->features & VK_SYNC_FEATURE_CPU_RESET); } } return VK_DEVICE_TIMELINE_MODE_ASSISTED; } static void collect_enabled_features(struct vk_device *device, const VkDeviceCreateInfo *pCreateInfo) { if (pCreateInfo->pEnabledFeatures) vk_set_physical_device_features_1_0(&device->enabled_features, pCreateInfo->pEnabledFeatures); vk_set_physical_device_features(&device->enabled_features, pCreateInfo->pNext); } static VkResult vk_device_memory_report_init(struct vk_device *device, const VkDeviceCreateInfo *pCreateInfo) { struct vk_device_memory_report *mem_reports = NULL; uint32_t count = 0; vk_foreach_struct_const(pnext, pCreateInfo->pNext) { if (pnext->sType == VK_STRUCTURE_TYPE_DEVICE_DEVICE_MEMORY_REPORT_CREATE_INFO_EXT) count++; } if (!count) return VK_SUCCESS; mem_reports = vk_alloc(&device->alloc, sizeof(*mem_reports) * count, 8, VK_SYSTEM_ALLOCATION_SCOPE_DEVICE); if (!mem_reports) return VK_ERROR_OUT_OF_HOST_MEMORY; count = 0; vk_foreach_struct_const(pnext, pCreateInfo->pNext) { if (pnext->sType == VK_STRUCTURE_TYPE_DEVICE_DEVICE_MEMORY_REPORT_CREATE_INFO_EXT) { const struct VkDeviceDeviceMemoryReportCreateInfoEXT *report = (void *)pnext; mem_reports[count].callback = report->pfnUserCallback; mem_reports[count].data = report->pUserData; count++; } } device->memory_report_count = count; device->memory_reports = mem_reports; return VK_SUCCESS; } VkResult vk_device_init(struct vk_device *device, struct vk_physical_device *physical_device, const struct vk_device_dispatch_table *dispatch_table, const VkDeviceCreateInfo *pCreateInfo, const VkAllocationCallbacks *alloc) { memset(device, 0, sizeof(*device)); vk_object_base_init(device, &device->base, VK_OBJECT_TYPE_DEVICE); if (alloc != NULL) device->alloc = *alloc; else device->alloc = physical_device->instance->alloc; device->physical = physical_device; if (dispatch_table) { device->dispatch_table = *dispatch_table; /* Add common entrypoints without overwriting driver-provided ones. */ vk_device_dispatch_table_from_entrypoints( &device->dispatch_table, &vk_common_device_entrypoints, false); } for (uint32_t i = 0; i < pCreateInfo->enabledExtensionCount; i++) { int idx; for (idx = 0; idx < VK_DEVICE_EXTENSION_COUNT; idx++) { if (strcmp(pCreateInfo->ppEnabledExtensionNames[i], vk_device_extensions[idx].extensionName) == 0) break; } if (idx >= VK_DEVICE_EXTENSION_COUNT) return vk_errorf(physical_device, VK_ERROR_EXTENSION_NOT_PRESENT, "%s not supported", pCreateInfo->ppEnabledExtensionNames[i]); if (!physical_device->supported_extensions.extensions[idx]) return vk_errorf(physical_device, VK_ERROR_EXTENSION_NOT_PRESENT, "%s not supported", pCreateInfo->ppEnabledExtensionNames[i]); #ifdef ANDROID_STRICT if (!vk_android_allowed_device_extensions.extensions[idx]) return vk_errorf(physical_device, VK_ERROR_EXTENSION_NOT_PRESENT, "%s not supported", pCreateInfo->ppEnabledExtensionNames[i]); #endif device->enabled_extensions.extensions[idx] = true; } VkResult result = vk_physical_device_check_device_features(physical_device, pCreateInfo); if (result != VK_SUCCESS) return result; collect_enabled_features(device, pCreateInfo); p_atomic_set(&device->private_data_next_index, 0); list_inithead(&device->queues); device->mem_cache = NULL; device->timeline_mode = get_timeline_mode(physical_device); switch (device->timeline_mode) { case VK_DEVICE_TIMELINE_MODE_NONE: case VK_DEVICE_TIMELINE_MODE_NATIVE: device->submit_mode = VK_QUEUE_SUBMIT_MODE_IMMEDIATE; break; case VK_DEVICE_TIMELINE_MODE_EMULATED: device->submit_mode = VK_QUEUE_SUBMIT_MODE_DEFERRED; break; case VK_DEVICE_TIMELINE_MODE_ASSISTED: if (os_get_option("MESA_VK_ENABLE_SUBMIT_THREAD")) { if (debug_get_bool_option("MESA_VK_ENABLE_SUBMIT_THREAD", false)) { device->submit_mode = VK_QUEUE_SUBMIT_MODE_THREADED; } else { device->submit_mode = VK_QUEUE_SUBMIT_MODE_IMMEDIATE; } } else { device->submit_mode = VK_QUEUE_SUBMIT_MODE_THREADED_ON_DEMAND; } break; default: UNREACHABLE("Invalid timeline mode"); } #ifdef VK_USE_PLATFORM_ANDROID_KHR mtx_init(&device->swapchain_private_mtx, mtx_plain); device->swapchain_private = NULL; #endif /* VK_USE_PLATFORM_ANDROID_KHR */ simple_mtx_init(&device->trace_mtx, mtx_plain); vk_foreach_struct_const (ext, pCreateInfo->pNext) { switch (ext->sType) { case VK_STRUCTURE_TYPE_DEVICE_PIPELINE_BINARY_INTERNAL_CACHE_CONTROL_KHR: { const VkDevicePipelineBinaryInternalCacheControlKHR *cache_control = (const void *)ext; if (cache_control->disableInternalCache) device->disable_internal_cache = true; break; } default: break; } } if (device->enabled_extensions.KHR_calibrated_timestamps || device->enabled_extensions.EXT_calibrated_timestamps) { /* sorted by preference */ const VkTimeDomainKHR calibrate_domains[] = { VK_TIME_DOMAIN_CLOCK_MONOTONIC_RAW_KHR, VK_TIME_DOMAIN_CLOCK_MONOTONIC_KHR, }; for (uint32_t i = 0; i < ARRAY_SIZE(calibrate_domains); i++) { const VkTimeDomainKHR domain = calibrate_domains[i]; uint64_t ts; if (vk_device_get_timestamp(NULL, domain, &ts) == VK_SUCCESS) { device->calibrate_time_domain = domain; break; } } assert(device->calibrate_time_domain != VK_TIME_DOMAIN_DEVICE_KHR); device->device_time_domain_period = (uint64_t)ceilf(device->physical->properties.timestampPeriod); } result = vk_device_memory_report_init(device, pCreateInfo); if (result != VK_SUCCESS) return result; return VK_SUCCESS; } static void vk_device_memory_report_finish(struct vk_device *device) { vk_free(&device->alloc, device->memory_reports); } void vk_device_finish(struct vk_device *device) { /* Drivers should tear down their own queues */ assert(list_is_empty(&device->queues)); if (device->sync) device->sync->finalize(device->sync); vk_device_memory_report_finish(device); vk_memory_trace_finish(device); #ifdef VK_USE_PLATFORM_ANDROID_KHR if (device->swapchain_private) { hash_table_foreach(device->swapchain_private, entry) util_sparse_array_finish(entry->data); ralloc_free(device->swapchain_private); } #endif /* VK_USE_PLATFORM_ANDROID_KHR */ simple_mtx_destroy(&device->trace_mtx); vk_object_base_finish(&device->base); } void vk_device_enable_threaded_submit(struct vk_device *device) { /* This must be called before any queues are created */ assert(list_is_empty(&device->queues)); /* In order to use threaded submit, we need every sync type that can be * used as a wait fence for vkQueueSubmit() to support WAIT_PENDING. * It's required for cross-thread/process submit re-ordering. */ for (const struct vk_sync_type *const *t = device->physical->supported_sync_types; *t; t++) { if ((*t)->features & VK_SYNC_FEATURE_GPU_WAIT) assert((*t)->features & VK_SYNC_FEATURE_WAIT_PENDING); } /* Any binary vk_sync types which will be used as permanent semaphore * payloads also need to support vk_sync_type::move, but that's a lot * harder to assert since it only applies to permanent semaphore payloads. */ if (device->submit_mode != VK_QUEUE_SUBMIT_MODE_THREADED) device->submit_mode = VK_QUEUE_SUBMIT_MODE_THREADED_ON_DEMAND; } VkResult vk_device_flush(struct vk_device *device) { if (device->submit_mode != VK_QUEUE_SUBMIT_MODE_DEFERRED) return VK_SUCCESS; bool progress; do { progress = false; vk_foreach_queue(queue, device) { uint32_t queue_submit_count; VkResult result = vk_queue_flush(queue, &queue_submit_count); if (unlikely(result != VK_SUCCESS)) return result; if (queue_submit_count) progress = true; } } while (progress); return VK_SUCCESS; } static const char * timeline_mode_str(struct vk_device *device) { switch (device->timeline_mode) { #define CASE(X) case VK_DEVICE_TIMELINE_MODE_##X: return #X; CASE(NONE) CASE(EMULATED) CASE(ASSISTED) CASE(NATIVE) #undef CASE default: return "UNKNOWN"; } } void _vk_device_report_lost(struct vk_device *device) { assert(p_atomic_read(&device->_lost.lost) > 0); device->_lost.reported = true; vk_foreach_queue(queue, device) { if (queue->_lost.lost) { __vk_errorf(queue, VK_ERROR_DEVICE_LOST, queue->_lost.error_file, queue->_lost.error_line, "%s", queue->_lost.error_msg); } } vk_logd(VK_LOG_OBJS(device), "Timeline mode is %s.", timeline_mode_str(device)); } VkResult _vk_device_set_lost(struct vk_device *device, const char *file, int line, const char *msg, ...) { /* This flushes out any per-queue device lost messages */ if (vk_device_is_lost(device)) return VK_ERROR_DEVICE_LOST; p_atomic_inc(&device->_lost.lost); device->_lost.reported = true; va_list ap; va_start(ap, msg); __vk_errorv(device, VK_ERROR_DEVICE_LOST, file, line, msg, ap); va_end(ap); vk_logd(VK_LOG_OBJS(device), "Timeline mode is %s.", timeline_mode_str(device)); if (debug_get_bool_option("MESA_VK_ABORT_ON_DEVICE_LOSS", false)) abort(); return VK_ERROR_DEVICE_LOST; } PFN_vkVoidFunction vk_device_get_proc_addr(const struct vk_device *device, const char *name) { if (device == NULL || name == NULL) return NULL; struct vk_instance *instance = device->physical->instance; return vk_device_dispatch_table_get_if_supported(&device->dispatch_table, name, instance->app_info.api_version, &instance->enabled_extensions, &device->enabled_extensions); } VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL vk_common_GetDeviceProcAddr(VkDevice _device, const char *pName) { VK_FROM_HANDLE(vk_device, device, _device); return vk_device_get_proc_addr(device, pName); } VKAPI_ATTR void VKAPI_CALL vk_common_GetDeviceQueue(VkDevice _device, uint32_t queueFamilyIndex, uint32_t queueIndex, VkQueue *pQueue) { VK_FROM_HANDLE(vk_device, device, _device); const VkDeviceQueueInfo2 info = { .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_INFO_2, .pNext = NULL, /* flags = 0 because (Vulkan spec 1.2.170 - vkGetDeviceQueue): * * "vkGetDeviceQueue must only be used to get queues that were * created with the flags parameter of VkDeviceQueueCreateInfo set * to zero. To get queues that were created with a non-zero flags * parameter use vkGetDeviceQueue2." */ .flags = 0, .queueFamilyIndex = queueFamilyIndex, .queueIndex = queueIndex, }; device->dispatch_table.GetDeviceQueue2(_device, &info, pQueue); } VKAPI_ATTR void VKAPI_CALL vk_common_GetDeviceQueue2(VkDevice _device, const VkDeviceQueueInfo2 *pQueueInfo, VkQueue *pQueue) { VK_FROM_HANDLE(vk_device, device, _device); struct vk_queue *queue = NULL; vk_foreach_queue(iter, device) { if (iter->queue_family_index == pQueueInfo->queueFamilyIndex && iter->index_in_family == pQueueInfo->queueIndex) { queue = iter; break; } } /* From the Vulkan 1.1.70 spec: * * "The queue returned by vkGetDeviceQueue2 must have the same flags * value from this structure as that used at device creation time in a * VkDeviceQueueCreateInfo instance. If no matching flags were specified * at device creation time then pQueue will return VK_NULL_HANDLE." */ if (queue && queue->flags == pQueueInfo->flags) *pQueue = vk_queue_to_handle(queue); else *pQueue = VK_NULL_HANDLE; } VKAPI_ATTR VkResult VKAPI_CALL vk_common_MapMemory(VkDevice _device, VkDeviceMemory memory, VkDeviceSize offset, VkDeviceSize size, VkMemoryMapFlags flags, void **ppData) { VK_FROM_HANDLE(vk_device, device, _device); const VkMemoryMapInfoKHR info = { .sType = VK_STRUCTURE_TYPE_MEMORY_MAP_INFO_KHR, .flags = flags, .memory = memory, .offset = offset, .size = size, }; return device->dispatch_table.MapMemory2KHR(_device, &info, ppData); } VKAPI_ATTR void VKAPI_CALL vk_common_UnmapMemory(VkDevice _device, VkDeviceMemory memory) { VK_FROM_HANDLE(vk_device, device, _device); ASSERTED VkResult result; const VkMemoryUnmapInfoKHR info = { .sType = VK_STRUCTURE_TYPE_MEMORY_UNMAP_INFO_KHR, .memory = memory, }; result = device->dispatch_table.UnmapMemory2KHR(_device, &info); assert(result == VK_SUCCESS); } VKAPI_ATTR void VKAPI_CALL vk_common_GetDeviceGroupPeerMemoryFeatures( VkDevice device, uint32_t heapIndex, uint32_t localDeviceIndex, uint32_t remoteDeviceIndex, VkPeerMemoryFeatureFlags *pPeerMemoryFeatures) { assert(localDeviceIndex == 0 && remoteDeviceIndex == 0); *pPeerMemoryFeatures = VK_PEER_MEMORY_FEATURE_COPY_SRC_BIT | VK_PEER_MEMORY_FEATURE_COPY_DST_BIT | VK_PEER_MEMORY_FEATURE_GENERIC_SRC_BIT | VK_PEER_MEMORY_FEATURE_GENERIC_DST_BIT; } VKAPI_ATTR void VKAPI_CALL vk_common_GetImageMemoryRequirements(VkDevice _device, VkImage image, VkMemoryRequirements *pMemoryRequirements) { VK_FROM_HANDLE(vk_device, device, _device); VkImageMemoryRequirementsInfo2 info = { .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2, .image = image, }; VkMemoryRequirements2 reqs = { .sType = VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2, }; device->dispatch_table.GetImageMemoryRequirements2(_device, &info, &reqs); *pMemoryRequirements = reqs.memoryRequirements; } VKAPI_ATTR VkResult VKAPI_CALL vk_common_BindImageMemory(VkDevice _device, VkImage image, VkDeviceMemory memory, VkDeviceSize memoryOffset) { VK_FROM_HANDLE(vk_device, device, _device); VkBindImageMemoryInfo bind = { .sType = VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_INFO, .image = image, .memory = memory, .memoryOffset = memoryOffset, }; return device->dispatch_table.BindImageMemory2(_device, 1, &bind); } VKAPI_ATTR void VKAPI_CALL vk_common_GetImageSparseMemoryRequirements(VkDevice _device, VkImage image, uint32_t *pSparseMemoryRequirementCount, VkSparseImageMemoryRequirements *pSparseMemoryRequirements) { VK_FROM_HANDLE(vk_device, device, _device); VkImageSparseMemoryRequirementsInfo2 info = { .sType = VK_STRUCTURE_TYPE_IMAGE_SPARSE_MEMORY_REQUIREMENTS_INFO_2, .image = image, }; if (!pSparseMemoryRequirements) { device->dispatch_table.GetImageSparseMemoryRequirements2(_device, &info, pSparseMemoryRequirementCount, NULL); return; } STACK_ARRAY(VkSparseImageMemoryRequirements2, mem_reqs2, *pSparseMemoryRequirementCount); for (unsigned i = 0; i < *pSparseMemoryRequirementCount; ++i) { mem_reqs2[i].sType = VK_STRUCTURE_TYPE_SPARSE_IMAGE_MEMORY_REQUIREMENTS_2; mem_reqs2[i].pNext = NULL; } device->dispatch_table.GetImageSparseMemoryRequirements2(_device, &info, pSparseMemoryRequirementCount, mem_reqs2); for (unsigned i = 0; i < *pSparseMemoryRequirementCount; ++i) pSparseMemoryRequirements[i] = mem_reqs2[i].memoryRequirements; STACK_ARRAY_FINISH(mem_reqs2); } VKAPI_ATTR VkResult VKAPI_CALL vk_common_DeviceWaitIdle(VkDevice _device) { MESA_TRACE_FUNC(); VK_FROM_HANDLE(vk_device, device, _device); const struct vk_device_dispatch_table *disp = &device->dispatch_table; vk_foreach_queue(queue, device) { VkResult result = disp->QueueWaitIdle(vk_queue_to_handle(queue)); if (result != VK_SUCCESS) return result; } return VK_SUCCESS; } VkResult vk_device_copy_semaphore_payloads(struct vk_device *device, uint32_t wait_semaphore_count, const VkSemaphoreSubmitInfo *wait_semaphores, uint32_t signal_semaphore_count, const VkSemaphoreSubmitInfo *signal_semaphores, uint32_t fence_count, const VkFence *fences) { if (device->copy_sync_payloads == NULL) return VK_ERROR_FEATURE_NOT_PRESENT; STACK_ARRAY(struct vk_sync_wait, waits, wait_semaphore_count); STACK_ARRAY(struct vk_sync_timeline_point *, wait_points, wait_semaphore_count); STACK_ARRAY(struct vk_sync *, resets, wait_semaphore_count); STACK_ARRAY(struct vk_sync_signal, signals, signal_semaphore_count + fence_count); STACK_ARRAY(struct vk_sync_timeline_point *, signal_points, signal_semaphore_count + fence_count); uint32_t wait_count = 0, reset_count = 0, signal_count = 0; VkResult result = VK_SUCCESS; for (uint32_t i = 0; i < wait_semaphore_count; i++) { VK_FROM_HANDLE(vk_semaphore, semaphore, wait_semaphores[i].semaphore); struct vk_sync_wait wait = { .sync = vk_semaphore_get_active_sync(semaphore), .stage_mask = wait_semaphores[i].stageMask, .wait_value = semaphore->type == VK_SEMAPHORE_TYPE_TIMELINE ? wait_semaphores[i].value : 0, }; struct vk_sync_timeline_point *wait_point = NULL; VkResult result = vk_sync_wait_unwrap(device, &wait, &wait_point); if (unlikely(result != VK_SUCCESS)) goto fail; if (wait.sync == NULL) continue; wait_points[wait_count] = wait_point; waits[wait_count] = wait; wait_count++; } for (uint32_t i = 0; i < signal_semaphore_count; i++) { VK_FROM_HANDLE(vk_semaphore, semaphore, signal_semaphores[i].semaphore); if (semaphore->type == VK_SEMAPHORE_TYPE_TIMELINE && signal_semaphores[i].value == 0) { result = vk_errorf(device, VK_ERROR_UNKNOWN, "Tried to signal a timeline with value 0"); goto fail; } struct vk_sync_signal signal = { .sync = vk_semaphore_get_active_sync(semaphore), .stage_mask = signal_semaphores[i].stageMask, .signal_value = semaphore->type == VK_SEMAPHORE_TYPE_TIMELINE ? signal_semaphores[i].value : 0, }; struct vk_sync_timeline_point *signal_point = NULL; VkResult result = vk_sync_signal_unwrap(device, &signal, &signal_point); if (unlikely(result != VK_SUCCESS)) goto fail; signal_points[signal_count] = signal_point; signals[signal_count] = signal; signal_count++; } for (uint32_t i = 0; i < fence_count; i++) { VK_FROM_HANDLE(vk_fence, fence, fences[i]); struct vk_sync_signal signal = { .sync = vk_fence_get_active_sync(fence), .stage_mask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT, }; struct vk_sync_timeline_point *signal_point = NULL; VkResult result = vk_sync_signal_unwrap(device, &signal, &signal_point); if (unlikely(result != VK_SUCCESS)) goto fail; /* Timeline fences aren't a thing */ assert(signal_point == NULL); signal_points[signal_count] = signal_point; signals[signal_count] = signal; signal_count++; } if (wait_count == 0) { /* Nothing to wait on. Just signal everything */ result = vk_sync_signal_many(device, signal_count, signals); if (result != VK_SUCCESS) goto fail; } else if (signal_count > 0) { /* Wait for time points to materialize */ result = vk_sync_wait_many(device, wait_count, waits, VK_SYNC_WAIT_PENDING, UINT64_MAX); if (result != VK_SUCCESS) goto fail; /* Now do the copy */ result = device->copy_sync_payloads(device, wait_count, waits, signal_count, signals); if (result != VK_SUCCESS) goto fail; } /* Reset any syncs which were waited on but not signaled */ for (uint32_t i = 0; i < wait_count; i++) { bool was_signaled = false; for (uint32_t j = 0; j < signal_count; j++) { if (signals[j].sync == waits[i].sync) { was_signaled = true; break; } } if (!was_signaled) resets[reset_count++] = waits[i].sync; } if (reset_count > 0) { result = vk_sync_reset_many(device, reset_count, resets); if (result != VK_SUCCESS) goto fail; } /* Reset any temporary semaphores we waited on */ for (uint32_t i = 0; i < wait_semaphore_count; i++) { VK_FROM_HANDLE(vk_semaphore, semaphore, wait_semaphores[i].semaphore); vk_semaphore_reset_temporary(device, semaphore); } /* Install time points */ for (uint32_t i = 0; i < signal_count; i++) { if (signal_points[i] == NULL) continue; vk_sync_timeline_point_install(device, signal_points[i]); /* Installing the point consumes our reference */ signal_points[i] = NULL; } fail: for (uint32_t i = 0; i < signal_count; i++) { if (signal_points[i] != NULL) vk_sync_timeline_point_unref(device, signal_points[i]); } STACK_ARRAY_FINISH(waits); STACK_ARRAY_FINISH(wait_points); STACK_ARRAY_FINISH(resets); STACK_ARRAY_FINISH(signals); STACK_ARRAY_FINISH(signal_points); return result; } VkResult vk_device_get_timestamp(struct vk_device *device, VkTimeDomainKHR domain, uint64_t *timestamp) { if (domain == VK_TIME_DOMAIN_DEVICE_KHR) { assert(device && device->get_timestamp); return device->get_timestamp(device, timestamp); } /* device is not used for host time domains */ #ifndef _WIN32 clockid_t clockid; struct timespec ts; switch (domain) { case VK_TIME_DOMAIN_CLOCK_MONOTONIC_KHR: clockid = CLOCK_MONOTONIC; break; case VK_TIME_DOMAIN_CLOCK_MONOTONIC_RAW_KHR: /* The "RAW" clocks on Linux are called "FAST" on FreeBSD */ #if defined(CLOCK_MONOTONIC_RAW) clockid = CLOCK_MONOTONIC_RAW; break; #elif defined(CLOCK_MONOTONIC_FAST) clockid = CLOCK_MONOTONIC_FAST; break; #else FALLTHROUGH; #endif default: goto fail; } if (clock_gettime(clockid, &ts) < 0) goto fail; *timestamp = (uint64_t)ts.tv_sec * NSEC_PER_SEC + ts.tv_nsec; return VK_SUCCESS; fail: #endif /* _WIN32 */ return VK_ERROR_FEATURE_NOT_PRESENT; } VKAPI_ATTR VkResult VKAPI_CALL vk_common_GetCalibratedTimestampsKHR( VkDevice _device, uint32_t timestampCount, const VkCalibratedTimestampInfoKHR *pTimestampInfos, uint64_t *pTimestamps, uint64_t *pMaxDeviation) { VK_FROM_HANDLE(vk_device, device, _device); uint64_t begin, end; VkResult result; /* collect timestamps as tight as possible */ result = vk_device_get_timestamp(device, device->calibrate_time_domain, &begin); for (uint32_t i = 0; i < timestampCount; i++) { const VkTimeDomainKHR domain = pTimestampInfos[i].timeDomain; if (domain == device->calibrate_time_domain) pTimestamps[i] = begin; else result |= vk_device_get_timestamp(device, domain, &pTimestamps[i]); } result |= vk_device_get_timestamp(device, device->calibrate_time_domain, &end); if (result != VK_SUCCESS) return VK_ERROR_OUT_OF_HOST_MEMORY; uint64_t max_clock_period = 0; for (uint32_t i = 0; i < timestampCount; i++) { const VkTimeDomainKHR domain = pTimestampInfos[i].timeDomain; const uint64_t period = domain == VK_TIME_DOMAIN_DEVICE_KHR ? device->device_time_domain_period : domain != device->calibrate_time_domain ? 1 : 0; max_clock_period = MAX2(max_clock_period, period); } *pMaxDeviation = vk_time_max_deviation(begin, end, max_clock_period); return VK_SUCCESS; } #ifndef _WIN32 uint64_t vk_clock_gettime(clockid_t clock_id) { struct timespec current; int ret; ret = clock_gettime(clock_id, ¤t); #ifdef CLOCK_MONOTONIC_RAW if (ret < 0 && clock_id == CLOCK_MONOTONIC_RAW) ret = clock_gettime(CLOCK_MONOTONIC, ¤t); #endif if (ret < 0) return 0; return (uint64_t)current.tv_sec * 1000000000ULL + current.tv_nsec; } #endif //!_WIN32