+
+VkResult anv_GetSemaphoreCounterValue(
+ VkDevice _device,
+ VkSemaphore _semaphore,
+ uint64_t* pValue)
+{
+ ANV_FROM_HANDLE(anv_device, device, _device);
+ ANV_FROM_HANDLE(anv_semaphore, semaphore, _semaphore);
+
+ struct anv_semaphore_impl *impl =
+ semaphore->temporary.type != ANV_SEMAPHORE_TYPE_NONE ?
+ &semaphore->temporary : &semaphore->permanent;
+
+ switch (impl->type) {
+ case ANV_SEMAPHORE_TYPE_TIMELINE: {
+ pthread_mutex_lock(&device->mutex);
+ *pValue = impl->timeline.highest_past;
+ pthread_mutex_unlock(&device->mutex);
+ return VK_SUCCESS;
+ }
+
+ default:
+ unreachable("Invalid semaphore type");
+ }
+}
+
+static VkResult
+anv_timeline_wait_locked(struct anv_device *device,
+ struct anv_timeline *timeline,
+ uint64_t serial, uint64_t abs_timeout_ns)
+{
+ /* Wait on the queue_submit condition variable until the timeline has a
+ * time point pending that's at least as high as serial.
+ */
+ while (timeline->highest_pending < serial) {
+ struct timespec abstime = {
+ .tv_sec = abs_timeout_ns / NSEC_PER_SEC,
+ .tv_nsec = abs_timeout_ns % NSEC_PER_SEC,
+ };
+
+ int ret = pthread_cond_timedwait(&device->queue_submit,
+ &device->mutex, &abstime);
+ assert(ret != EINVAL);
+ if (anv_gettime_ns() >= abs_timeout_ns &&
+ timeline->highest_pending < serial)
+ return VK_TIMEOUT;
+ }
+
+ while (1) {
+ VkResult result = anv_timeline_gc_locked(device, timeline);
+ if (result != VK_SUCCESS)
+ return result;
+
+ if (timeline->highest_past >= serial)
+ return VK_SUCCESS;
+
+ /* If we got here, our earliest time point has a busy BO */
+ struct anv_timeline_point *point =
+ list_first_entry(&timeline->points,
+ struct anv_timeline_point, link);
+
+ /* Drop the lock while we wait. */
+ point->waiting++;
+ pthread_mutex_unlock(&device->mutex);
+
+ result = anv_device_wait(device, point->bo,
+ anv_get_relative_timeout(abs_timeout_ns));
+
+ /* Pick the mutex back up */
+ pthread_mutex_lock(&device->mutex);
+ point->waiting--;
+
+ /* This covers both VK_TIMEOUT and VK_ERROR_DEVICE_LOST */
+ if (result != VK_SUCCESS)
+ return result;
+ }
+}
+
+static VkResult
+anv_timelines_wait(struct anv_device *device,
+ struct anv_timeline **timelines,
+ const uint64_t *serials,
+ uint32_t n_timelines,
+ bool wait_all,
+ uint64_t abs_timeout_ns)
+{
+ if (!wait_all && n_timelines > 1) {
+ pthread_mutex_lock(&device->mutex);
+
+ while (1) {
+ VkResult result;
+ for (uint32_t i = 0; i < n_timelines; i++) {
+ result =
+ anv_timeline_wait_locked(device, timelines[i], serials[i], 0);
+ if (result != VK_TIMEOUT)
+ break;
+ }
+
+ if (result != VK_TIMEOUT ||
+ anv_gettime_ns() >= abs_timeout_ns) {
+ pthread_mutex_unlock(&device->mutex);
+ return result;
+ }
+
+ /* If none of them are ready do a short wait so we don't completely
+ * spin while holding the lock. The 10us is completely arbitrary.
+ */
+ uint64_t abs_short_wait_ns =
+ anv_get_absolute_timeout(
+ MIN2((anv_gettime_ns() - abs_timeout_ns) / 10, 10 * 1000));
+ struct timespec abstime = {
+ .tv_sec = abs_short_wait_ns / NSEC_PER_SEC,
+ .tv_nsec = abs_short_wait_ns % NSEC_PER_SEC,
+ };
+ ASSERTED int ret;
+ ret = pthread_cond_timedwait(&device->queue_submit,
+ &device->mutex, &abstime);
+ assert(ret != EINVAL);
+ }
+ } else {
+ VkResult result = VK_SUCCESS;
+ pthread_mutex_lock(&device->mutex);
+ for (uint32_t i = 0; i < n_timelines; i++) {
+ result =
+ anv_timeline_wait_locked(device, timelines[i],
+ serials[i], abs_timeout_ns);
+ if (result != VK_SUCCESS)
+ break;
+ }
+ pthread_mutex_unlock(&device->mutex);
+ return result;
+ }
+}
+
+VkResult anv_WaitSemaphores(
+ VkDevice _device,
+ const VkSemaphoreWaitInfoKHR* pWaitInfo,
+ uint64_t timeout)
+{
+ ANV_FROM_HANDLE(anv_device, device, _device);
+
+ if (device->no_hw)
+ return VK_SUCCESS;
+
+ struct anv_timeline **timelines =
+ vk_alloc(&device->vk.alloc,
+ pWaitInfo->semaphoreCount * sizeof(*timelines),
+ 8, VK_SYSTEM_ALLOCATION_SCOPE_COMMAND);
+ if (!timelines)
+ return vk_error(VK_ERROR_OUT_OF_HOST_MEMORY);
+
+ uint64_t *values = vk_alloc(&device->vk.alloc,
+ pWaitInfo->semaphoreCount * sizeof(*values),
+ 8, VK_SYSTEM_ALLOCATION_SCOPE_COMMAND);
+ if (!values) {
+ vk_free(&device->vk.alloc, timelines);
+ return vk_error(VK_ERROR_OUT_OF_HOST_MEMORY);
+ }
+
+ uint32_t handle_count = 0;
+ for (uint32_t i = 0; i < pWaitInfo->semaphoreCount; i++) {
+ ANV_FROM_HANDLE(anv_semaphore, semaphore, pWaitInfo->pSemaphores[i]);
+ struct anv_semaphore_impl *impl =
+ semaphore->temporary.type != ANV_SEMAPHORE_TYPE_NONE ?
+ &semaphore->temporary : &semaphore->permanent;
+
+ assert(impl->type == ANV_SEMAPHORE_TYPE_TIMELINE);
+
+ if (pWaitInfo->pValues[i] == 0)
+ continue;
+
+ timelines[handle_count] = &impl->timeline;
+ values[handle_count] = pWaitInfo->pValues[i];
+ handle_count++;
+ }
+
+ VkResult result = VK_SUCCESS;
+ if (handle_count > 0) {
+ result = anv_timelines_wait(device, timelines, values, handle_count,
+ !(pWaitInfo->flags & VK_SEMAPHORE_WAIT_ANY_BIT_KHR),
+ anv_get_absolute_timeout(timeout));
+ }
+
+ vk_free(&device->vk.alloc, timelines);
+ vk_free(&device->vk.alloc, values);
+
+ return result;
+}
+
+VkResult anv_SignalSemaphore(
+ VkDevice _device,
+ const VkSemaphoreSignalInfoKHR* pSignalInfo)
+{
+ ANV_FROM_HANDLE(anv_device, device, _device);
+ ANV_FROM_HANDLE(anv_semaphore, semaphore, pSignalInfo->semaphore);
+
+ struct anv_semaphore_impl *impl =
+ semaphore->temporary.type != ANV_SEMAPHORE_TYPE_NONE ?
+ &semaphore->temporary : &semaphore->permanent;
+
+ switch (impl->type) {
+ case ANV_SEMAPHORE_TYPE_TIMELINE: {
+ pthread_mutex_lock(&device->mutex);
+
+ VkResult result = anv_timeline_gc_locked(device, &impl->timeline);
+
+ assert(pSignalInfo->value > impl->timeline.highest_pending);
+
+ impl->timeline.highest_pending = impl->timeline.highest_past = pSignalInfo->value;
+
+ if (result == VK_SUCCESS)
+ result = anv_device_submit_deferred_locked(device);
+
+ pthread_cond_broadcast(&device->queue_submit);
+ pthread_mutex_unlock(&device->mutex);
+ return result;
+ }
+
+ default:
+ unreachable("Invalid semaphore type");
+ }
+}