[Mesa-dev] [PATCH 18/21] anv: Implement support for exporting semaphores as FENCE_FD
Jason Ekstrand
jason at jlekstrand.net
Fri Apr 14 17:38:05 UTC 2017
---
src/intel/vulkan/anv_batch_chain.c | 96 ++++++++++++++++++++++++++++++++++++--
src/intel/vulkan/anv_device.c | 25 ++++++++++
src/intel/vulkan/anv_gem.c | 36 ++++++++++++++
src/intel/vulkan/anv_private.h | 24 +++++++---
src/intel/vulkan/anv_queue.c | 73 +++++++++++++++++++++++++++--
5 files changed, 240 insertions(+), 14 deletions(-)
diff --git a/src/intel/vulkan/anv_batch_chain.c b/src/intel/vulkan/anv_batch_chain.c
index 0529f22..ec37c81 100644
--- a/src/intel/vulkan/anv_batch_chain.c
+++ b/src/intel/vulkan/anv_batch_chain.c
@@ -1387,6 +1387,23 @@ setup_execbuf_for_cmd_buffer(struct anv_execbuf *execbuf,
return VK_SUCCESS;
}
+static void
+setup_empty_execbuf(struct anv_execbuf *execbuf, struct anv_device *device)
+{
+ anv_execbuf_add_bo(execbuf, &device->trivial_batch_bo, NULL, 0,
+ &device->alloc);
+
+ execbuf->execbuf = (struct drm_i915_gem_execbuffer2) {
+ .buffers_ptr = (uintptr_t) execbuf->objects,
+ .buffer_count = execbuf->bo_count,
+ .batch_start_offset = 0,
+ .batch_len = 8, /* GEN8_MI_BATCH_BUFFER_END and NOOP */
+ .flags = I915_EXEC_HANDLE_LUT | I915_EXEC_RENDER,
+ .rsvd1 = device->context_id,
+ .rsvd2 = 0,
+ };
+}
+
VkResult
anv_cmd_buffer_execbuf(struct anv_device *device,
struct anv_cmd_buffer *cmd_buffer,
@@ -1398,11 +1415,13 @@ anv_cmd_buffer_execbuf(struct anv_device *device,
struct anv_execbuf execbuf;
anv_execbuf_init(&execbuf);
+ int in_fence = -1;
VkResult result = VK_SUCCESS;
for (uint32_t i = 0; i < num_in_semaphores; i++) {
ANV_FROM_HANDLE(anv_semaphore, semaphore, in_semaphores[i]);
- assert(semaphore->temporary.type == ANV_SEMAPHORE_TYPE_NONE);
- struct anv_semaphore_impl *impl = &semaphore->permanent;
+ struct anv_semaphore_impl *impl =
+ semaphore->temporary.type != ANV_SEMAPHORE_TYPE_NONE ?
+ &semaphore->temporary : &semaphore->permanent;
switch (impl->type) {
case ANV_SEMAPHORE_TYPE_BO:
@@ -1411,13 +1430,42 @@ anv_cmd_buffer_execbuf(struct anv_device *device,
if (result != VK_SUCCESS)
return result;
break;
+
+ case ANV_SEMAPHORE_TYPE_SYNC_FILE:
+ if (in_fence == -1) {
+ in_fence = impl->fd;
+ } else {
+ int merge = anv_gem_sync_file_merge(device, in_fence, impl->fd);
+ if (merge == -1)
+ return vk_error(VK_ERROR_INVALID_EXTERNAL_HANDLE_KHX);
+
+ close(impl->fd);
+ close(in_fence);
+ in_fence = merge;
+ }
+
+ impl->fd = -1;
+ break;
+
default:
break;
}
+
+ /* Waiting on a semaphore with temporary state implicitly resets it back
+ * to the permanent state.
+ */
+ if (semaphore->temporary.type != ANV_SEMAPHORE_TYPE_NONE) {
+ assert(semaphore->temporary.type == ANV_SEMAPHORE_TYPE_SYNC_FILE);
+ semaphore->temporary.type = ANV_SEMAPHORE_TYPE_NONE;
+ }
}
+ bool need_out_fence = false;
for (uint32_t i = 0; i < num_out_semaphores; i++) {
ANV_FROM_HANDLE(anv_semaphore, semaphore, out_semaphores[i]);
+ /* Out fences can't have temporary state because that would imply
+ * that we imported a sync file and are trying to signal it.
+ */
assert(semaphore->temporary.type == ANV_SEMAPHORE_TYPE_NONE);
struct anv_semaphore_impl *impl = &semaphore->permanent;
@@ -1428,17 +1476,55 @@ anv_cmd_buffer_execbuf(struct anv_device *device,
if (result != VK_SUCCESS)
return result;
break;
+
+ case ANV_SEMAPHORE_TYPE_SYNC_FILE:
+ need_out_fence = true;
+ break;
+
default:
break;
}
}
- result = setup_execbuf_for_cmd_buffer(&execbuf, cmd_buffer);
- if (result != VK_SUCCESS)
- return result;
+ if (cmd_buffer) {
+ result = setup_execbuf_for_cmd_buffer(&execbuf, cmd_buffer);
+ if (result != VK_SUCCESS)
+ return result;
+ } else {
+ setup_empty_execbuf(&execbuf, device);
+ }
+
+ if (in_fence != -1) {
+ execbuf.execbuf.flags |= I915_EXEC_FENCE_IN;
+ execbuf.execbuf.rsvd2 |= (uint32_t)in_fence;
+ }
+
+ if (need_out_fence)
+ execbuf.execbuf.flags |= I915_EXEC_FENCE_OUT;
result = anv_device_execbuf(device, &execbuf.execbuf, execbuf.bos);
+ /* Execbuf does not consume the in_fence. It's our job to close it. */
+ close(in_fence);
+
+ if (result == VK_SUCCESS && need_out_fence) {
+ int out_fence = execbuf.execbuf.rsvd2 >> 32;
+ for (uint32_t i = 0; i < num_out_semaphores; i++) {
+ ANV_FROM_HANDLE(anv_semaphore, semaphore, out_semaphores[i]);
+ /* Out fences can't have temporary state because that would imply
+ * that we imported a sync file and are trying to signal it.
+ */
+ assert(semaphore->temporary.type == ANV_SEMAPHORE_TYPE_NONE);
+ struct anv_semaphore_impl *impl = &semaphore->permanent;
+
+ if (impl->type == ANV_SEMAPHORE_TYPE_SYNC_FILE) {
+ assert(impl->fd == -1);
+ impl->fd = dup(out_fence);
+ }
+ }
+ close(out_fence);
+ }
+
anv_execbuf_finish(&execbuf, &device->alloc);
return result;
diff --git a/src/intel/vulkan/anv_device.c b/src/intel/vulkan/anv_device.c
index f6e77ab..2885bb6 100644
--- a/src/intel/vulkan/anv_device.c
+++ b/src/intel/vulkan/anv_device.c
@@ -232,6 +232,7 @@ anv_physical_device_init(struct anv_physical_device *device,
goto fail;
device->has_exec_async = anv_gem_get_param(fd, I915_PARAM_HAS_EXEC_ASYNC);
+ device->has_exec_fence = anv_gem_get_param(fd, I915_PARAM_HAS_EXEC_FENCE);
bool swizzled = anv_gem_get_bit6_swizzle(fd, I915_TILING_X);
@@ -993,6 +994,26 @@ anv_device_init_border_colors(struct anv_device *device)
border_colors);
}
+static void
+anv_device_init_trivial_batch(struct anv_device *device)
+{
+ anv_bo_init_new(&device->trivial_batch_bo, device, 4096);
+ void *map = anv_gem_mmap(device, device->trivial_batch_bo.gem_handle,
+ 0, 4096, 0);
+
+ struct anv_batch batch;
+ batch.start = batch.next = map;
+ batch.end = map + 4096;
+
+ anv_batch_emit(&batch, GEN7_MI_BATCH_BUFFER_END, bbe);
+ anv_batch_emit(&batch, GEN7_MI_NOOP, noop);
+
+ if (!device->info.has_llc)
+ anv_clflush_range(map, batch.next - map);
+
+ anv_gem_munmap(map, device->trivial_batch_bo.size);
+}
+
VkResult anv_CreateDevice(
VkPhysicalDevice physicalDevice,
const VkDeviceCreateInfo* pCreateInfo,
@@ -1116,6 +1137,8 @@ VkResult anv_CreateDevice(
if (result != VK_SUCCESS)
goto fail_surface_state_pool;
+ anv_device_init_trivial_batch(device);
+
anv_scratch_pool_init(device, &device->scratch_pool);
anv_queue_init(device, &device->queue);
@@ -1205,6 +1228,8 @@ void anv_DestroyDevice(
anv_gem_munmap(device->workaround_bo.map, device->workaround_bo.size);
anv_gem_close(device, device->workaround_bo.gem_handle);
+ anv_gem_close(device, device->trivial_batch_bo.gem_handle);
+
anv_state_pool_finish(&device->surface_state_pool);
anv_block_pool_finish(&device->surface_state_block_pool);
anv_state_pool_finish(&device->instruction_state_pool);
diff --git a/src/intel/vulkan/anv_gem.c b/src/intel/vulkan/anv_gem.c
index 1392bf4..ffdc5a1 100644
--- a/src/intel/vulkan/anv_gem.c
+++ b/src/intel/vulkan/anv_gem.c
@@ -22,6 +22,7 @@
*/
#include <sys/ioctl.h>
+#include <sys/types.h>
#include <sys/mman.h>
#include <string.h>
#include <errno.h>
@@ -400,3 +401,38 @@ anv_gem_fd_to_handle(struct anv_device *device, int fd)
return args.handle;
}
+
+#ifndef SYNC_IOC_MAGIC
+/* duplicated from linux/sync_file.h to avoid build-time depnedency
+ * on new (v4.7) kernel headers. Once distro's are mostly using
+ * something newer than v4.7 drop this and #include <linux/sync_file.h>
+ * instead.
+ */
+struct sync_merge_data {
+ char name[32];
+ __s32 fd2;
+ __s32 fence;
+ __u32 flags;
+ __u32 pad;
+};
+
+#define SYNC_IOC_MAGIC '>'
+#define SYNC_IOC_MERGE _IOWR(SYNC_IOC_MAGIC, 3, struct sync_merge_data)
+#endif
+
+int
+anv_gem_sync_file_merge(struct anv_device *device, int fd1, int fd2)
+{
+ const char name[] = "anv merge fence";
+ struct sync_merge_data args = {
+ .fd2 = fd2,
+ .fence = -1,
+ };
+ memcpy(args.name, name, sizeof(name));
+
+ int ret = anv_ioctl(fd1, SYNC_IOC_MERGE, &args);
+ if (ret == -1)
+ return -1;
+
+ return args.fence;
+}
diff --git a/src/intel/vulkan/anv_private.h b/src/intel/vulkan/anv_private.h
index 4a59cac..a083a07 100644
--- a/src/intel/vulkan/anv_private.h
+++ b/src/intel/vulkan/anv_private.h
@@ -647,6 +647,7 @@ struct anv_physical_device {
struct isl_device isl_dev;
int cmd_parser_version;
bool has_exec_async;
+ bool has_exec_fence;
uint32_t eu_total;
uint32_t subslice_total;
@@ -733,6 +734,7 @@ struct anv_device {
struct anv_state_pool surface_state_pool;
struct anv_bo workaround_bo;
+ struct anv_bo trivial_batch_bo;
struct anv_pipeline_cache blorp_shader_cache;
struct blorp_context blorp;
@@ -797,6 +799,7 @@ uint32_t anv_gem_fd_to_handle(struct anv_device *device, int fd);
int anv_gem_set_caching(struct anv_device *device, uint32_t gem_handle, uint32_t caching);
int anv_gem_set_domain(struct anv_device *device, uint32_t gem_handle,
uint32_t read_domains, uint32_t write_domain);
+int anv_gem_sync_file_merge(struct anv_device *device, int fd1, int fd2);
VkResult anv_bo_init_new(struct anv_bo *bo, struct anv_device *device, uint64_t size);
@@ -1714,17 +1717,26 @@ enum anv_semaphore_type {
ANV_SEMAPHORE_TYPE_NONE = 0,
ANV_SEMAPHORE_TYPE_DUMMY,
ANV_SEMAPHORE_TYPE_BO,
+ ANV_SEMAPHORE_TYPE_SYNC_FILE,
};
struct anv_semaphore_impl {
enum anv_semaphore_type type;
- /* A BO representing this semaphore when type == ANV_SEMAPHORE_TYPE_BO.
- * This BO will be added to the object list on any execbuf2 calls for
- * which this semaphore is used as a wait or signal fence. When used as
- * a signal fence, the EXEC_OBJECT_WRITE flag will be set.
- */
- struct anv_bo *bo;
+ union {
+ /* A BO representing this semaphore when type == ANV_SEMAPHORE_TYPE_BO.
+ * This BO will be added to the object list on any execbuf2 calls for
+ * which this semaphore is used as a wait or signal fence. When used as
+ * a signal fence, the EXEC_OBJECT_WRITE flag will be set.
+ */
+ struct anv_bo *bo;
+
+ /* The sync file descriptor when type == AKV_SEMAPHORE_TYPE_SYNC_FILE.
+ * If the semaphore is in the unsignaled state due to either just being
+ * created or because it has been used for a wait, fd will be -1.
+ */
+ int fd;
+ };
};
struct anv_semaphore {
diff --git a/src/intel/vulkan/anv_queue.c b/src/intel/vulkan/anv_queue.c
index 980fd2d..e7c8ef5 100644
--- a/src/intel/vulkan/anv_queue.c
+++ b/src/intel/vulkan/anv_queue.c
@@ -159,6 +159,23 @@ VkResult anv_QueueSubmit(
pthread_mutex_lock(&device->mutex);
for (uint32_t i = 0; i < submitCount; i++) {
+ if (pSubmits[i].commandBufferCount == 0) {
+ /* If we don't have any command buffers, we need to submit a dummy
+ * batch to give GEM something to wait on. We could, potentially,
+ * come up with something more efficient but this shouldn't be a
+ * common case.
+ */
+ result = anv_cmd_buffer_execbuf(device, NULL,
+ pSubmits[i].pWaitSemaphores,
+ pSubmits[i].waitSemaphoreCount,
+ pSubmits[i].pSignalSemaphores,
+ pSubmits[i].signalSemaphoreCount);
+ if (result != VK_SUCCESS)
+ goto out;
+
+ continue;
+ }
+
for (uint32_t j = 0; j < pSubmits[i].commandBufferCount; j++) {
ANV_FROM_HANDLE(anv_cmd_buffer, cmd_buffer,
pSubmits[i].pCommandBuffers[j]);
@@ -554,6 +571,11 @@ VkResult anv_CreateSemaphore(
* EXEC_OBJECT_ASYNC bit set.
*/
semaphore->permanent.bo->flags &= ~EXEC_OBJECT_ASYNC;
+ } else if (handleTypes & VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_FENCE_FD_BIT_KHX) {
+ assert(handleTypes == VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_FENCE_FD_BIT_KHX);
+
+ semaphore->permanent.type = ANV_SEMAPHORE_TYPE_SYNC_FILE;
+ semaphore->permanent.fd = -1;
} else {
assert(!"Unknown handle type");
vk_free2(&device->alloc, pAllocator, semaphore);
@@ -581,6 +603,10 @@ anv_semaphore_impl_cleanup(struct anv_device *device,
anv_bo_cache_release(device, &device->bo_cache, impl->bo);
break;
+ case ANV_SEMAPHORE_TYPE_SYNC_FILE:
+ close(impl->fd);
+ break;
+
default:
unreachable("Invalid semaphore type");
}
@@ -608,6 +634,8 @@ void anv_GetPhysicalDeviceExternalSemaphorePropertiesKHX(
const VkPhysicalDeviceExternalSemaphoreInfoKHX* pExternalSemaphoreInfo,
VkExternalSemaphorePropertiesKHX* pExternalSemaphoreProperties)
{
+ ANV_FROM_HANDLE(anv_physical_device, device, physicalDevice);
+
switch (pExternalSemaphoreInfo->handleType) {
case VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT_KHX:
pExternalSemaphoreProperties->exportFromImportedHandleTypes = 0;
@@ -616,13 +644,27 @@ void anv_GetPhysicalDeviceExternalSemaphorePropertiesKHX(
pExternalSemaphoreProperties->externalSemaphoreFeatures =
VK_EXTERNAL_SEMAPHORE_FEATURE_EXPORTABLE_BIT_KHX |
VK_EXTERNAL_SEMAPHORE_FEATURE_IMPORTABLE_BIT_KHX;
+ return;
+
+ case VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_FENCE_FD_BIT_KHX:
+ if (device->has_exec_fence) {
+ pExternalSemaphoreProperties->exportFromImportedHandleTypes = 0;
+ pExternalSemaphoreProperties->compatibleHandleTypes =
+ VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_FENCE_FD_BIT_KHX;
+ pExternalSemaphoreProperties->externalSemaphoreFeatures =
+ VK_EXTERNAL_SEMAPHORE_FEATURE_EXPORTABLE_BIT_KHX |
+ VK_EXTERNAL_SEMAPHORE_FEATURE_IMPORTABLE_BIT_KHX;
+ return;
+ }
break;
default:
- pExternalSemaphoreProperties->exportFromImportedHandleTypes = 0;
- pExternalSemaphoreProperties->compatibleHandleTypes = 0;
- pExternalSemaphoreProperties->externalSemaphoreFeatures = 0;
+ break;
}
+
+ pExternalSemaphoreProperties->exportFromImportedHandleTypes = 0;
+ pExternalSemaphoreProperties->compatibleHandleTypes = 0;
+ pExternalSemaphoreProperties->externalSemaphoreFeatures = 0;
}
VkResult anv_ImportSemaphoreFdKHX(
@@ -654,6 +696,14 @@ VkResult anv_ImportSemaphoreFdKHX(
return VK_SUCCESS;
}
+ case VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_FENCE_FD_BIT_KHX:
+ anv_semaphore_impl_cleanup(device, &semaphore->temporary);
+
+ semaphore->temporary.type = ANV_SEMAPHORE_TYPE_SYNC_FILE;
+ semaphore->temporary.fd = pImportSemaphoreFdInfo->fd;
+
+ return VK_SUCCESS;
+
default:
return vk_error(VK_ERROR_INVALID_EXTERNAL_HANDLE_KHX);
}
@@ -673,6 +723,23 @@ VkResult anv_GetSemaphoreFdKHX(
return anv_bo_cache_export(device, &device->bo_cache,
semaphore->permanent.bo, pFd);
+ case ANV_SEMAPHORE_TYPE_SYNC_FILE:
+ /* There are two reasons why this could happen:
+ *
+ * 1) The user is trying to export without submitting something that
+ * signals the semaphore. If this is the case, it's their bug so
+ * what we return here doesn't matter.
+ *
+ * 2) The kernel didn't give us a file descriptor. The most likely
+ * reason for this is running out of file descriptors.
+ */
+ if (semaphore->permanent.fd < 0)
+ return vk_error(VK_ERROR_TOO_MANY_OBJECTS);
+
+ *pFd = semaphore->permanent.fd;
+ semaphore->permanent.fd = -1;
+ return VK_SUCCESS;
+
default:
return vk_error(VK_ERROR_INVALID_EXTERNAL_HANDLE_KHX);
}
--
2.5.0.400.gff86faf
More information about the mesa-dev
mailing list