diff --git a/doc/changes/ipc/mr.478.2.md b/doc/changes/ipc/mr.478.2.md new file mode 100644 index 000000000..76bf6a343 --- /dev/null +++ b/doc/changes/ipc/mr.478.2.md @@ -0,0 +1,3 @@ +client: Add a "loopback" image allocator, this code allocates a swapchain from +the service then imports that back to the service as if it was imported. This +tests both the import code and the image allocator code. diff --git a/src/xrt/ipc/ipc_client_compositor.c b/src/xrt/ipc/ipc_client_compositor.c index d85e7d509..d748ccb4a 100644 --- a/src/xrt/ipc/ipc_client_compositor.c +++ b/src/xrt/ipc/ipc_client_compositor.c @@ -34,6 +34,10 @@ * Internal structs and helpers. * */ + +//! Define to test the loopback allocator. +#undef IPC_USE_LOOPBACK_IMAGE_ALLOCATOR + /*! * Client proxy for an xrt_compositor_native implementation over IPC. * @implements xrt_compositor_native @@ -56,6 +60,11 @@ struct ipc_client_compositor enum xrt_blend_mode env_blend_mode; } layers; + +#ifdef IPC_USE_LOOPBACK_IMAGE_ALLOCATOR + //! To test image allocator. + struct xrt_image_native_allocator loopback_xina; +#endif }; /*! @@ -556,6 +565,115 @@ ipc_compositor_destroy(struct xrt_compositor *xc) } +/* + * + * Loopback image allocator. + * + */ + +#ifdef IPC_USE_LOOPBACK_IMAGE_ALLOCATOR +static inline xrt_result_t +ipc_compositor_images_allocate(struct xrt_image_native_allocator *xina, + const struct xrt_swapchain_create_info *xsci, + size_t in_num_images, + struct xrt_image_native *out_images) +{ + struct ipc_client_compositor *icc = + container_of(xina, struct ipc_client_compositor, loopback_xina); + + int remote_fds[IPC_MAX_SWAPCHAIN_FDS] = {0}; + xrt_result_t r = XRT_SUCCESS; + uint32_t num_images; + uint32_t handle; + uint64_t size; + + for (size_t i = 0; ARRAY_SIZE(remote_fds); i++) { + remote_fds[i] = -1; + } + + for (size_t i = 0; in_num_images; i++) { + out_images[i].fd = -1; + out_images[i].size = 0; + } + + r = ipc_call_swapchain_create(icc->ipc_c, // connection + xsci, // in + &handle, // out + &num_images, // out + &size, // out + remote_fds, // fds + IPC_MAX_SWAPCHAIN_FDS); // fds + if (r != XRT_SUCCESS) { + return r; + } + + /* + * It's okay to destroy it immediately, the native handles are + * now owned by us and we keep the buffers alive that way. + */ + r = ipc_call_swapchain_destroy(icc->ipc_c, handle); + assert(r == XRT_SUCCESS); + + // Clumsy way of handling this. + if (num_images < in_num_images) { + for (uint32_t k = 0; k < num_images && k < in_num_images; k++) { + /* + * Paranoia, we do know that any fd not touched by + * ipc_call_swapchain_create will be -1. + */ + if (remote_fds[k] >= 0) { + close(remote_fds[k]); + remote_fds[k] = -1; + } + } + + return XRT_ERROR_IPC_FAILURE; + } + + // Copy up to in_num_images, or num_images what ever is lowest. + uint32_t i = 0; + for (; i < num_images && i < in_num_images; i++) { + out_images[i].fd = remote_fds[i]; + out_images[i].size = size; + } + + // Close any fds we are not interested in. + for (; i < num_images; i++) { + /* + * Paranoia, we do know that any fd not touched by + * ipc_call_swapchain_create will be -1. + */ + if (remote_fds[i] >= 0) { + close(remote_fds[i]); + remote_fds[i] = -1; + } + } + + return XRT_SUCCESS; +} + +static inline xrt_result_t +ipc_compositor_images_free(struct xrt_image_native_allocator *xina, + size_t num_images, + struct xrt_image_native *out_images) +{ + for (uint32_t i = 0; i < num_images; i++) { + close(out_images[i].fd); + out_images[i].fd = -1; + out_images[i].size = 0; + } + + return XRT_SUCCESS; +} + +static inline void +ipc_compositor_images_destroy(struct xrt_image_native_allocator *xina) +{ + // Noop +} +#endif + + /* * * 'Exported' functions. @@ -589,6 +707,17 @@ ipc_client_compositor_create(struct ipc_connection *ipc_c, c->ipc_c = ipc_c; c->xina = xina; + +#ifdef IPC_USE_LOOPBACK_IMAGE_ALLOCATOR + c->loopback_xina.images_allocate = ipc_compositor_images_allocate; + c->loopback_xina.images_free = ipc_compositor_images_free; + c->loopback_xina.destroy = ipc_compositor_images_destroy; + + if (c->xina == NULL) { + c->xina = &c->loopback_xina; + } +#endif + // Fetch info from the compositor, among it the format format list. get_info(&(c->base.base), &c->base.base.info);