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