+ goto err_destroy_sync_primitive;
+
+ memcpy(batch_bo->map, batch->start, size);
+ if (!device->info.has_llc)
+ gen_flush_range(batch_bo->map, size);
+
+ submit->simple_bo = batch_bo;
+ submit->simple_bo_size = size;
+ }
+
+ result = _anv_queue_submit(queue, &submit, true);
+
+ if (result == VK_SUCCESS) {
+ if (has_syncobj_wait) {
+ if (anv_gem_syncobj_wait(device, &syncobj, 1,
+ anv_get_absolute_timeout(INT64_MAX), true))
+ result = anv_device_set_lost(device, "anv_gem_syncobj_wait failed: %m");
+ anv_gem_syncobj_destroy(device, syncobj);
+ } else {
+ result = anv_device_wait(device, sync_bo,
+ anv_get_relative_timeout(INT64_MAX));
+ anv_device_release_bo(device, sync_bo);
+ }
+ }
+
+ if (batch)
+ anv_bo_pool_free(&device->batch_bo_pool, batch_bo);
+
+ if (submit)
+ anv_queue_submit_free(device, submit);
+
+ return result;
+
+ err_destroy_sync_primitive:
+ if (has_syncobj_wait)
+ anv_gem_syncobj_destroy(device, syncobj);
+ else
+ anv_device_release_bo(device, sync_bo);
+ err_free_submit:
+ if (submit)
+ anv_queue_submit_free(device, submit);
+
+ return result;
+}
+
+/* Transfer ownership of temporary semaphores from the VkSemaphore object to
+ * the anv_queue_submit object. Those temporary semaphores are then freed in
+ * anv_queue_submit_free() once the driver is finished with them.
+ */
+static VkResult
+maybe_transfer_temporary_semaphore(struct anv_queue_submit *submit,
+ struct anv_semaphore *semaphore,
+ struct anv_semaphore_impl **out_impl)
+{
+ struct anv_semaphore_impl *impl = &semaphore->temporary;
+
+ if (impl->type == ANV_SEMAPHORE_TYPE_NONE) {
+ *out_impl = &semaphore->permanent;
+ return VK_SUCCESS;
+ }
+
+ /* BO backed timeline semaphores cannot be temporary. */
+ assert(impl->type != ANV_SEMAPHORE_TYPE_TIMELINE);
+
+ /*
+ * There is a requirement to reset semaphore to their permanent state after
+ * submission. From the Vulkan 1.0.53 spec:
+ *
+ * "If the import is temporary, the implementation must restore the
+ * semaphore to its prior permanent state after submitting the next
+ * semaphore wait operation."
+ *
+ * In the case we defer the actual submission to a thread because of the
+ * wait-before-submit behavior required for timeline semaphores, we need to
+ * make copies of the temporary syncobj to ensure they stay alive until we
+ * do the actual execbuffer ioctl.
+ */
+ if (submit->temporary_semaphore_count >= submit->temporary_semaphore_array_length) {
+ uint32_t new_len = MAX2(submit->temporary_semaphore_array_length * 2, 8);
+ /* Make sure that if the realloc fails, we still have the old semaphore
+ * array around to properly clean things up on failure.
+ */
+ struct anv_semaphore_impl *new_array =
+ vk_realloc(submit->alloc,
+ submit->temporary_semaphores,
+ new_len * sizeof(*submit->temporary_semaphores),
+ 8, submit->alloc_scope);
+ if (new_array == NULL)
+ return vk_error(VK_ERROR_OUT_OF_HOST_MEMORY);
+
+ submit->temporary_semaphores = new_array;
+ submit->temporary_semaphore_array_length = new_len;
+ }
+
+ /* Copy anv_semaphore_impl into anv_queue_submit. */
+ submit->temporary_semaphores[submit->temporary_semaphore_count++] = *impl;
+ *out_impl = &submit->temporary_semaphores[submit->temporary_semaphore_count - 1];
+
+ /* Clear the incoming semaphore */
+ impl->type = ANV_SEMAPHORE_TYPE_NONE;
+
+ return VK_SUCCESS;
+}
+
+static VkResult
+anv_queue_submit(struct anv_queue *queue,
+ struct anv_cmd_buffer *cmd_buffer,
+ const VkSemaphore *in_semaphores,
+ const uint64_t *in_values,
+ uint32_t num_in_semaphores,
+ const VkSemaphore *out_semaphores,
+ const uint64_t *out_values,
+ uint32_t num_out_semaphores,
+ struct anv_bo *wsi_signal_bo,
+ VkFence _fence,
+ int perf_query_pass)
+{
+ ANV_FROM_HANDLE(anv_fence, fence, _fence);
+ struct anv_device *device = queue->device;
+ UNUSED struct anv_physical_device *pdevice = device->physical;
+ struct anv_queue_submit *submit = anv_queue_submit_alloc(device, perf_query_pass);
+ if (!submit)
+ return vk_error(VK_ERROR_OUT_OF_HOST_MEMORY);
+
+ submit->cmd_buffer = cmd_buffer;
+
+ VkResult result = VK_SUCCESS;
+
+ for (uint32_t i = 0; i < num_in_semaphores; i++) {
+ ANV_FROM_HANDLE(anv_semaphore, semaphore, in_semaphores[i]);
+ struct anv_semaphore_impl *impl;
+
+ result = maybe_transfer_temporary_semaphore(submit, semaphore, &impl);
+ if (result != VK_SUCCESS)
+ goto error;
+
+ switch (impl->type) {
+ case ANV_SEMAPHORE_TYPE_BO:
+ assert(!pdevice->has_syncobj);
+ result = anv_queue_submit_add_fence_bo(submit, impl->bo, false /* signal */);
+ if (result != VK_SUCCESS)
+ goto error;
+ break;
+
+ case ANV_SEMAPHORE_TYPE_WSI_BO:
+ /* When using a window-system buffer as a semaphore, always enable
+ * EXEC_OBJECT_WRITE. This gives us a WaR hazard with the display or
+ * compositor's read of the buffer and enforces that we don't start
+ * rendering until they are finished. This is exactly the
+ * synchronization we want with vkAcquireNextImage.
+ */
+ result = anv_queue_submit_add_fence_bo(submit, impl->bo, true /* signal */);
+ if (result != VK_SUCCESS)
+ goto error;
+ break;
+
+ case ANV_SEMAPHORE_TYPE_SYNC_FILE:
+ assert(!pdevice->has_syncobj);
+ if (submit->in_fence == -1) {
+ submit->in_fence = impl->fd;
+ if (submit->in_fence == -1) {
+ result = vk_error(VK_ERROR_INVALID_EXTERNAL_HANDLE);
+ goto error;
+ }
+ impl->fd = -1;
+ } else {
+ int merge = anv_gem_sync_file_merge(device, submit->in_fence, impl->fd);
+ if (merge == -1) {
+ result = vk_error(VK_ERROR_INVALID_EXTERNAL_HANDLE);
+ goto error;
+ }
+ close(impl->fd);
+ close(submit->in_fence);
+ impl->fd = -1;
+ submit->in_fence = merge;
+ }
+ break;
+
+ case ANV_SEMAPHORE_TYPE_DRM_SYNCOBJ: {
+ result = anv_queue_submit_add_syncobj(submit, device,
+ impl->syncobj,
+ I915_EXEC_FENCE_WAIT);
+ if (result != VK_SUCCESS)
+ goto error;
+ break;
+ }
+
+ case ANV_SEMAPHORE_TYPE_TIMELINE:
+ result = anv_queue_submit_add_timeline_wait(submit, device,
+ &impl->timeline,
+ in_values ? in_values[i] : 0);
+ if (result != VK_SUCCESS)
+ goto error;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ for (uint32_t i = 0; i < num_out_semaphores; i++) {
+ ANV_FROM_HANDLE(anv_semaphore, semaphore, out_semaphores[i]);
+
+ /* Under most circumstances, out fences won't be temporary. However,
+ * the spec does allow it for opaque_fd. From the Vulkan 1.0.53 spec:
+ *
+ * "If the import is temporary, the implementation must restore the
+ * semaphore to its prior permanent state after submitting the next
+ * semaphore wait operation."
+ *
+ * The spec says nothing whatsoever about signal operations on
+ * temporarily imported semaphores so it appears they are allowed.
+ * There are also CTS tests that require this to work.
+ */
+ struct anv_semaphore_impl *impl =
+ semaphore->temporary.type != ANV_SEMAPHORE_TYPE_NONE ?
+ &semaphore->temporary : &semaphore->permanent;
+
+ switch (impl->type) {
+ case ANV_SEMAPHORE_TYPE_BO:
+ assert(!pdevice->has_syncobj);
+ result = anv_queue_submit_add_fence_bo(submit, impl->bo, true /* signal */);
+ if (result != VK_SUCCESS)
+ goto error;
+ break;
+
+ case ANV_SEMAPHORE_TYPE_SYNC_FILE:
+ assert(!pdevice->has_syncobj);
+ result = anv_queue_submit_add_sync_fd_fence(submit, semaphore);
+ if (result != VK_SUCCESS)
+ goto error;
+ break;
+
+ case ANV_SEMAPHORE_TYPE_DRM_SYNCOBJ: {
+ result = anv_queue_submit_add_syncobj(submit, device, impl->syncobj,
+ I915_EXEC_FENCE_SIGNAL);
+ if (result != VK_SUCCESS)
+ goto error;
+ break;
+ }
+
+ case ANV_SEMAPHORE_TYPE_TIMELINE:
+ result = anv_queue_submit_add_timeline_signal(submit, device,
+ &impl->timeline,
+ out_values ? out_values[i] : 0);
+ if (result != VK_SUCCESS)
+ goto error;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if (wsi_signal_bo) {
+ result = anv_queue_submit_add_fence_bo(submit, wsi_signal_bo, true /* signal */);
+ if (result != VK_SUCCESS)
+ goto error;
+ }
+
+ if (fence) {
+ /* Under most circumstances, out fences won't be temporary. However,
+ * the spec does allow it for opaque_fd. From the Vulkan 1.0.53 spec:
+ *
+ * "If the import is temporary, the implementation must restore the
+ * semaphore to its prior permanent state after submitting the next
+ * semaphore wait operation."
+ *
+ * The spec says nothing whatsoever about signal operations on
+ * temporarily imported semaphores so it appears they are allowed.
+ * There are also CTS tests that require this to work.
+ */
+ struct anv_fence_impl *impl =
+ fence->temporary.type != ANV_FENCE_TYPE_NONE ?
+ &fence->temporary : &fence->permanent;
+
+ switch (impl->type) {
+ case ANV_FENCE_TYPE_BO:
+ result = anv_queue_submit_add_fence_bo(submit, impl->bo.bo, true /* signal */);
+ if (result != VK_SUCCESS)
+ goto error;
+ break;
+
+ case ANV_FENCE_TYPE_SYNCOBJ: {
+ /*
+ * For the same reason we reset the signaled binary syncobj above,
+ * also reset the fence's syncobj so that they don't contain a
+ * signaled dma-fence.
+ */
+ result = anv_queue_submit_add_syncobj(submit, device, impl->syncobj,
+ I915_EXEC_FENCE_SIGNAL);
+ if (result != VK_SUCCESS)
+ goto error;
+ break;
+ }
+
+ default:
+ unreachable("Invalid fence type");
+ }
+ }