The open source OpenXR runtime
at mr/scanout-values 558 lines 13 kB view raw
1// Copyright 2020-2023, Collabora, Ltd. 2// SPDX-License-Identifier: BSL-1.0 3/*! 4 * @file 5 * @brief Common server side code. 6 * @author Pete Black <pblack@collabora.com> 7 * @author Jakob Bornecrantz <jakob@collabora.com> 8 * @author Rylie Pavlik <rylie.pavlik@collabora.com> 9 * @ingroup ipc_server 10 */ 11 12#pragma once 13 14#include "xrt/xrt_compiler.h" 15#include "xrt/xrt_limits.h" 16#include "xrt/xrt_space.h" 17#include "xrt/xrt_system.h" 18 19#include "os/os_threading.h" 20 21#include "util/u_logging.h" 22 23#include "shared/ipc_protocol.h" 24#include "shared/ipc_message_channel.h" 25 26#include <stdio.h> 27 28 29#ifdef __cplusplus 30extern "C" { 31#endif 32 33/* 34 * 35 * Logging 36 * 37 */ 38 39#define IPC_TRACE(d, ...) U_LOG_IFL_T(d->log_level, __VA_ARGS__) 40#define IPC_DEBUG(d, ...) U_LOG_IFL_D(d->log_level, __VA_ARGS__) 41#define IPC_INFO(d, ...) U_LOG_IFL_I(d->log_level, __VA_ARGS__) 42#define IPC_WARN(d, ...) U_LOG_IFL_W(d->log_level, __VA_ARGS__) 43#define IPC_ERROR(d, ...) U_LOG_IFL_E(d->log_level, __VA_ARGS__) 44 45#define IPC_CHK_AND_RET(S, ...) U_LOG_CHK_AND_RET((S)->log_level, __VA_ARGS__) 46#define IPC_CHK_WITH_GOTO(S, ...) U_LOG_CHK_WITH_GOTO((S)->log_level, __VA_ARGS__) 47#define IPC_CHK_WITH_RET(S, ...) U_LOG_CHK_WITH_RET((S)->log_level, __VA_ARGS__) 48#define IPC_CHK_ONLY_PRINT(S, ...) U_LOG_CHK_ONLY_PRINT((S)->log_level, __VA_ARGS__) 49#define IPC_CHK_ALWAYS_RET(S, ...) U_LOG_CHK_ALWAYS_RET((S)->log_level, __VA_ARGS__) 50 51 52/* 53 * 54 * Structs 55 * 56 */ 57 58#define IPC_MAX_CLIENT_SEMAPHORES 8 59#define IPC_MAX_CLIENT_SWAPCHAINS (XRT_MAX_LAYERS * 2) 60#define IPC_MAX_CLIENT_SPACES 128 61#define IPC_MAX_CLIENT_FUTURES 128 62 63struct xrt_instance; 64struct xrt_compositor; 65struct xrt_compositor_native; 66 67 68/*! 69 * Information about a single swapchain. 70 * 71 * @ingroup ipc_server 72 */ 73struct ipc_swapchain_data 74{ 75 uint32_t width; 76 uint32_t height; 77 uint64_t format; 78 uint32_t image_count; 79 80 bool active; 81}; 82 83/*! 84 * Holds the state for a single client. 85 * 86 * @ingroup ipc_server 87 */ 88struct ipc_client_state 89{ 90 //! Link back to the main server. 91 struct ipc_server *server; 92 93 //! Session for this client. 94 struct xrt_session *xs; 95 96 //! Compositor for this client. 97 struct xrt_compositor *xc; 98 99 //! Is the inputs and outputs active. 100 bool io_active; 101 102 //! Number of swapchains in use by client 103 uint32_t swapchain_count; 104 105 //! Ptrs to the swapchains 106 struct xrt_swapchain *xscs[IPC_MAX_CLIENT_SWAPCHAINS]; 107 108 //! Data for the swapchains. 109 struct ipc_swapchain_data swapchain_data[IPC_MAX_CLIENT_SWAPCHAINS]; 110 111 //! Number of compositor semaphores in use by client 112 uint32_t compositor_semaphore_count; 113 114 //! Ptrs to the semaphores. 115 struct xrt_compositor_semaphore *xcsems[IPC_MAX_CLIENT_SEMAPHORES]; 116 117 //! Ptrs to the futures. 118 struct xrt_future *xfts[IPC_MAX_CLIENT_FUTURES]; 119 120 struct 121 { 122 uint32_t root; 123 uint32_t local; 124 uint32_t stage; 125 uint32_t unbounded; 126 } semantic_spaces; 127 128 //! Number of spaces. 129 uint32_t space_count; 130 //! Index of localspace in ipc client. 131 uint32_t local_space_index; 132 //! Index of localspace in space overseer. 133 uint32_t local_space_overseer_index; 134 //! Index of localfloorspace in ipc client. 135 uint32_t local_floor_space_index; 136 //! Index of localfloorspace in space overseer. 137 uint32_t local_floor_space_overseer_index; 138 139 //! Ptrs to the spaces. 140 struct xrt_space *xspcs[IPC_MAX_CLIENT_SPACES]; 141 142 //! Which of the references spaces is the client using. 143 bool ref_space_used[XRT_SPACE_REFERENCE_TYPE_COUNT]; 144 145 //! Which of the device features is the client using. 146 bool device_feature_used[XRT_DEVICE_FEATURE_MAX_ENUM]; 147 148 //! Socket fd used for client comms 149 struct ipc_message_channel imc; 150 151 struct ipc_app_state client_state; 152 153 154 uint64_t plane_detection_size; 155 uint64_t plane_detection_count; 156 157 //! Array of plane detection ids with plane_detection_size entries. 158 uint64_t *plane_detection_ids; 159 160 //! Array of xrt_devices with plane_detection_size entries. 161 struct xrt_device **plane_detection_xdev; 162 163 int server_thread_index; 164 165 xrt_shmem_handle_t ism_handle; 166}; 167 168enum ipc_thread_state 169{ 170 IPC_THREAD_READY, 171 IPC_THREAD_STARTING, 172 IPC_THREAD_RUNNING, 173 IPC_THREAD_STOPPING, 174}; 175 176struct ipc_thread 177{ 178 struct os_thread thread; 179 volatile enum ipc_thread_state state; 180 volatile struct ipc_client_state ics; 181}; 182 183 184/*! 185 * 186 */ 187struct ipc_device 188{ 189 //! The actual device. 190 struct xrt_device *xdev; 191 192 //! Is the IO suppressed for this device. 193 bool io_active; 194}; 195 196/*! 197 * Platform-specific mainloop object for the IPC server. 198 * 199 * Contents are essentially implementation details, but are listed in full here so they may be included by value in the 200 * main ipc_server struct. 201 * 202 * @see ipc_design 203 * 204 * @ingroup ipc_server 205 */ 206struct ipc_server_mainloop 207{ 208 209#if defined(XRT_OS_ANDROID) || defined(XRT_OS_LINUX) || defined(XRT_DOXYGEN) 210 //! For waiting on various events in the main thread. 211 int epoll_fd; 212#endif 213 214#if defined(XRT_OS_ANDROID) || defined(XRT_DOXYGEN) 215 /*! 216 * @name Android Mainloop Members 217 * @{ 218 */ 219 220 //! File descriptor for the read end of our pipe for submitting new clients 221 int pipe_read; 222 223 /*! 224 * File descriptor for the write end of our pipe for submitting new clients 225 * 226 * Must hold client_push_mutex while writing. 227 */ 228 int pipe_write; 229 230 /*! 231 * Mutex for being able to register oneself as a new client. 232 * 233 * Locked only by threads in `ipc_server_mainloop_add_fd()`. 234 * 235 * This must be locked first, and kept locked the entire time a client is attempting to register and wait for 236 * confirmation. It ensures no acknowledgements of acceptance are lost and moves the overhead of ensuring this 237 * to the client thread. 238 */ 239 pthread_mutex_t client_push_mutex; 240 241 242 /*! 243 * The last client fd we accepted, to acknowledge client acceptance. 244 * 245 * Also used as a sentinel during shutdown. 246 * 247 * Must hold accept_mutex while writing. 248 */ 249 int last_accepted_fd; 250 251 /*! 252 * Condition variable for accepting clients. 253 * 254 * Signalled when @ref last_accepted_fd is updated. 255 * 256 * Associated with @ref accept_mutex 257 */ 258 pthread_cond_t accept_cond; 259 260 /*! 261 * Mutex for accepting clients. 262 * 263 * Locked by both clients and server: that is, by threads in `ipc_server_mainloop_add_fd()` and in the 264 * server/compositor thread in an implementation function called from `ipc_server_mainloop_poll()`. 265 * 266 * Exists to operate in conjunction with @ref accept_cond - it exists to make sure that the client can be woken 267 * when the server accepts it. 268 */ 269 pthread_mutex_t accept_mutex; 270 271 272 /*! @} */ 273#define XRT_IPC_GOT_IMPL 274#endif 275 276#if (defined(XRT_OS_LINUX) && !defined(XRT_OS_ANDROID)) || defined(XRT_DOXYGEN) 277 /*! 278 * @name Desktop Linux Mainloop Members 279 * @{ 280 */ 281 282 //! Socket that we accept connections on. 283 int listen_socket; 284 285 //! Were we launched by socket activation, instead of explicitly? 286 bool launched_by_socket; 287 288 //! The socket filename we bound to, if any. 289 char *socket_filename; 290 291 /*! @} */ 292 293#define XRT_IPC_GOT_IMPL 294#endif 295 296#if defined(XRT_OS_WINDOWS) || defined(XRT_DOXYGEN) 297 /*! 298 * @name Desktop Windows Mainloop Members 299 * @{ 300 */ 301 302 //! Named Pipe that we accept connections on. 303 HANDLE pipe_handle; 304 305 //! Name of the Pipe that we accept connections on. 306 char *pipe_name; 307 308 /*! @} */ 309 310#define XRT_IPC_GOT_IMPL 311#endif 312 313#ifndef XRT_IPC_GOT_IMPL 314#error "Need port" 315#endif 316}; 317 318/*! 319 * De-initialize the mainloop object. 320 * @public @memberof ipc_server_mainloop 321 */ 322void 323ipc_server_mainloop_deinit(struct ipc_server_mainloop *ml); 324 325/*! 326 * Initialize the mainloop object. 327 * 328 * @return <0 on error. 329 * @public @memberof ipc_server_mainloop 330 */ 331int 332ipc_server_mainloop_init(struct ipc_server_mainloop *ml); 333 334/*! 335 * @brief Poll the mainloop. 336 * 337 * Any errors are signalled by calling ipc_server_handle_failure() 338 * @public @memberof ipc_server_mainloop 339 */ 340void 341ipc_server_mainloop_poll(struct ipc_server *vs, struct ipc_server_mainloop *ml); 342 343/*! 344 * Main IPC object for the server. 345 * 346 * @ingroup ipc_server 347 */ 348struct ipc_server 349{ 350 struct xrt_instance *xinst; 351 352 //! Handle for the current process, e.g. pidfile on linux 353 struct u_process *process; 354 355 struct u_debug_gui *debug_gui; 356 357 //! The @ref xrt_iface level system. 358 struct xrt_system *xsys; 359 360 //! System devices. 361 struct xrt_system_devices *xsysd; 362 363 //! Space overseer. 364 struct xrt_space_overseer *xso; 365 366 //! System compositor. 367 struct xrt_system_compositor *xsysc; 368 369 struct ipc_device idevs[XRT_SYSTEM_MAX_DEVICES]; 370 struct xrt_tracking_origin *xtracks[XRT_SYSTEM_MAX_DEVICES]; 371 372 struct ipc_shared_memory *isms[IPC_MAX_CLIENTS]; 373 374 struct ipc_server_mainloop ml; 375 376 // Is the mainloop supposed to run. 377 volatile bool running; 378 379 // Should we exit when a client disconnects. 380 bool exit_on_disconnect; 381 382 // Should we exit when no clients are connected. 383 bool exit_when_idle; 384 385 // Timestamp when last client disconnected (for exit_when_idle delay) 386 uint64_t last_client_disconnect_ns; 387 388 // How long to wait after all clients disconnect before exiting (in nanoseconds) 389 uint64_t exit_when_idle_delay_ns; 390 391 enum u_logging_level log_level; 392 393 struct ipc_thread threads[IPC_MAX_CLIENTS]; 394 395 volatile uint32_t current_slot_index; 396 397 //! Generator for IDs. 398 uint32_t id_generator; 399 400 struct 401 { 402 int active_client_index; 403 int last_active_client_index; 404 405 // Counter for total number of connected clients 406 uint32_t connected_client_count; 407 408 struct os_mutex lock; 409 } global_state; 410}; 411 412 413/*! 414 * Get the current state of a client. 415 * 416 * @ingroup ipc_server 417 */ 418xrt_result_t 419ipc_server_get_client_app_state(struct ipc_server *s, uint32_t client_id, struct ipc_app_state *out_ias); 420 421/*! 422 * Set the new active client. 423 * 424 * @ingroup ipc_server 425 */ 426xrt_result_t 427ipc_server_set_active_client(struct ipc_server *s, uint32_t client_id); 428 429/*! 430 * Toggle the io for this client. 431 * 432 * @ingroup ipc_server 433 */ 434xrt_result_t 435ipc_server_toggle_io_client(struct ipc_server *s, uint32_t client_id); 436 437/*! 438 * Called by client threads to set a session to active. 439 * 440 * @ingroup ipc_server 441 */ 442void 443ipc_server_activate_session(volatile struct ipc_client_state *ics); 444 445/*! 446 * Called by client threads to set a session to deactivate. 447 * 448 * @ingroup ipc_server 449 */ 450void 451ipc_server_deactivate_session(volatile struct ipc_client_state *ics); 452 453/*! 454 * Called by client threads to recalculate active client. 455 * 456 * @ingroup ipc_server 457 */ 458void 459ipc_server_update_state(struct ipc_server *s); 460 461/*! 462 * Thread function for the client side dispatching. 463 * 464 * @ingroup ipc_server 465 */ 466void * 467ipc_server_client_thread(void *_ics); 468 469/*! 470 * This destroys the native compositor for this client and any extra objects 471 * created from it, like all of the swapchains. 472 */ 473void 474ipc_server_client_destroy_session_and_compositor(volatile struct ipc_client_state *ics); 475 476/*! 477 * @defgroup ipc_server_internals Server Internals 478 * @brief These are only called by the platform-specific mainloop polling code. 479 * @ingroup ipc_server 480 * @{ 481 */ 482/*! 483 * Called when a client has connected, it takes the client's ipc handle. 484 * Handles all things needed to be done for a client connecting, like starting 485 * it's thread. 486 * 487 * @param vs The IPC server. 488 * @param ipc_handle Handle to communicate over. 489 * @memberof ipc_server 490 */ 491void 492ipc_server_handle_client_connected(struct ipc_server *vs, xrt_ipc_handle_t ipc_handle); 493 494/*! 495 * Perform whatever needs to be done when the mainloop polling encounters a failure. 496 * @memberof ipc_server 497 */ 498void 499ipc_server_handle_failure(struct ipc_server *vs); 500 501/*! 502 * Perform whatever needs to be done when the mainloop polling identifies that the server should be shut down. 503 * 504 * Does something like setting a flag or otherwise signalling for shutdown: does not itself explicitly exit. 505 * @memberof ipc_server 506 */ 507void 508ipc_server_handle_shutdown_signal(struct ipc_server *vs); 509 510xrt_result_t 511ipc_server_get_system_properties(struct ipc_server *vs, struct xrt_system_properties *out_properties); 512//! @} 513 514/* 515 * 516 * Helpers 517 * 518 */ 519 520/*! 521 * Get a xdev with the given device_id. 522 */ 523static inline struct xrt_device * 524get_xdev(volatile struct ipc_client_state *ics, uint32_t device_id) 525{ 526 return ics->server->idevs[device_id].xdev; 527} 528 529/*! 530 * Get a idev with the given device_id. 531 */ 532static inline struct ipc_device * 533get_idev(volatile struct ipc_client_state *ics, uint32_t device_id) 534{ 535 return &ics->server->idevs[device_id]; 536} 537 538/*! 539 * Get the data in the shared memory of the given client. 540 */ 541static inline struct ipc_shared_memory * 542get_ism(volatile struct ipc_client_state *ics) 543{ 544 return ics->server->isms[ics->server_thread_index]; 545} 546 547/*! 548 * Get the handle for the shared memory of the given client. 549 */ 550static inline xrt_shmem_handle_t 551get_ism_handle(volatile struct ipc_client_state *ics) 552{ 553 return ics->ism_handle; 554} 555 556#ifdef __cplusplus 557} 558#endif