From 3223e9d8065a09e46d084b81500d83f7db6827d5 Mon Sep 17 00:00:00 2001 From: Ryan Pavlik <ryan.pavlik@collabora.com> Date: Fri, 15 Jul 2022 15:12:03 -0500 Subject: [PATCH] doc: Add info on graphics IPC to the IPC document --- doc/ipc-design.md | 56 ++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 50 insertions(+), 6 deletions(-) diff --git a/doc/ipc-design.md b/doc/ipc-design.md index 6b92572b6..6fb79ea66 100644 --- a/doc/ipc-design.md +++ b/doc/ipc-design.md @@ -1,11 +1,11 @@ -# IPC Design {#ipc-design} +# IPC Design and Implementation {#ipc-design} <!-- -Copyright 2021, Collabora, Ltd. and the Monado contributors +Copyright 2021-2022, Collabora, Ltd. and the Monado contributors SPDX-License-Identifier: BSL-1.0 --> -- Last updated: 8-December-2021 +- Last updated: 15-July-2022 When the service starts, an `xrt_instance` is created and selected, a native system compositor is initialized, a shared memory segment for device data is @@ -16,9 +16,13 @@ There are three main communication needs: - The client shared library needs to be able to **locate** a running service, if any, to start communication. (Auto-starting, where available, is handled by platform-specific mechanisms: the client currently has no code to explicitly - start up the service.) + start up the service.) This location mechanism must be able to establish or + share the RPC channel and shared memory access, often by passing a socket, + handle, or file descriptor. - The client and service must share a dedicated channel for IPC calls (also - known as **RPC** - remote procedure call), typically a socket. + known as **RPC** - remote procedure call), typically a socket. Importantly, + the channel must be able to carry both data messages and native graphics + buffer/sync handles (file descriptors, HANDLEs, AHardwareBuffers) - The service must share device data updating at various rates, shared by all clients. This is typically done with a form of **shared memory**. @@ -36,7 +40,7 @@ This socket is polled in the service mainloop, using epoll, to detect any new client connections. Upon a client connection to this "locating" socket, the service will [accept][] -the connection, returning an FD, which is passed to +the connection, returning a file descriptor (FD), which is passed to `start_client_listener_thread()` to start a thread specific to that client. The FD produced this way is now also used for the IPC calls - the **RPC** function - since it is specific to that client-server communication channel. One of the @@ -180,3 +184,43 @@ However, it is undesirable for the clients to be able to block the compositor/server, so this wait was considered not acceptable. Instead, the `ipc_server_mainloop::client_push_mutex` is used so that at most one un-acknowledged client may have written to the pipe at any given time. + +## A Note on Graphics IPC + +The IPC mechanisms described previously are used solely for small data. Graphics +data communication between application/client and server is done through sharing +of buffers and synchronization primitives, without any copying or serialization +of buffers within a frame loop. + +We use the system and graphics API provided mechanisms of sharing graphics +buffers and sync primitives, which all result in some cross-API-usable handle +type (generically processed as the types @ref xrt_graphics_buffer_handle_t and +@ref xrt_graphics_sync_handle_t). On all supported platforms, there exist ways +to share these handle types both within and between processes: + +- Linux and Android can send these handles, uniformly represented as file + descriptors, through a domain socket with a [SCM_RIGHTS][] message. +- It is anticipated that Windows will use DuplicateHandle and send handle + numbers to achieve an equivalent result. ([reference][win32handles]) While + recent versions of Windows have added `AF_UNIX` domain socket support, + [`SCM_RIGHTS` is not supported][WinSCM_RIGHTS]. + +The @ref xrt_compositor_native and @ref xrt_swapchain_native interfaces conceal +the compositor's own graphics API choice, interacting with a client compositor +solely through these generic handles. As such, even in single-process mode, +buffers and sync primitives are generally exported to handles and imported back +into another graphics API. (There is a small exception to this general statement +to allow in-process execution on a software Vulkan implementation for CI +purposes.) + +Generally, when possible, we allocate buffers on the server side in Vulkan, and +import into the client compositor and API. On Android, to support application +quotas and limits on allocation, etc, the client side allocates the buffer using +a @ref xrt_image_native_allocator (aka XINA) and shares it to the server. When +using D3D11 or D3D12 on Windows, buffers are allocated by the client compositor +and imported into the native compositor, because Vulkan can import buffers from +D3D, but D3D cannot import buffers allocated by Vulkan. + +[SCM_RIGHTS]: https://man7.org/linux/man-pages/man3/cmsg.3.html +[win32handles]: https://lackingrhoticity.blogspot.com/2015/05/passing-fds-handles-between-processes.html +[WinSCM_RIGHTS]: https://devblogs.microsoft.com/commandline/af_unix-comes-to-windows/#unsupportedunavailable