The open source OpenXR runtime
at prediction-2 586 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 //! Is the IO suppressed for this device. 199 bool io_active; 200}; 201 202/*! 203 * Platform-specific mainloop object for the IPC server. 204 * 205 * Contents are essentially implementation details, but are listed in full here so they may be included by value in the 206 * main ipc_server struct. 207 * 208 * @see ipc_design 209 * 210 * @ingroup ipc_server 211 */ 212struct ipc_server_mainloop 213{ 214 215#if defined(XRT_OS_ANDROID) || defined(XRT_OS_LINUX) || defined(XRT_DOXYGEN) 216 //! For waiting on various events in the main thread. 217 int epoll_fd; 218#endif 219 220#if defined(XRT_OS_ANDROID) || defined(XRT_DOXYGEN) 221 /*! 222 * @name Android Mainloop Members 223 * @{ 224 */ 225 226 //! File descriptor for the read end of our pipe for submitting new clients 227 int pipe_read; 228 229 /*! 230 * File descriptor for the write end of our pipe for submitting new clients 231 * 232 * Must hold client_push_mutex while writing. 233 */ 234 int pipe_write; 235 236 /*! 237 * Mutex for being able to register oneself as a new client. 238 * 239 * Locked only by threads in `ipc_server_mainloop_add_fd()`. 240 * 241 * This must be locked first, and kept locked the entire time a client is attempting to register and wait for 242 * confirmation. It ensures no acknowledgements of acceptance are lost and moves the overhead of ensuring this 243 * to the client thread. 244 */ 245 pthread_mutex_t client_push_mutex; 246 247 248 /*! 249 * The last client fd we accepted, to acknowledge client acceptance. 250 * 251 * Also used as a sentinel during shutdown. 252 * 253 * Must hold accept_mutex while writing. 254 */ 255 int last_accepted_fd; 256 257 /*! 258 * Condition variable for accepting clients. 259 * 260 * Signalled when @ref last_accepted_fd is updated. 261 * 262 * Associated with @ref accept_mutex 263 */ 264 pthread_cond_t accept_cond; 265 266 /*! 267 * Mutex for accepting clients. 268 * 269 * Locked by both clients and server: that is, by threads in `ipc_server_mainloop_add_fd()` and in the 270 * server/compositor thread in an implementation function called from `ipc_server_mainloop_poll()`. 271 * 272 * Exists to operate in conjunction with @ref accept_cond - it exists to make sure that the client can be woken 273 * when the server accepts it. 274 */ 275 pthread_mutex_t accept_mutex; 276 277 278 /*! @} */ 279#define XRT_IPC_GOT_IMPL 280#endif 281 282#if (defined(XRT_OS_LINUX) && !defined(XRT_OS_ANDROID)) || defined(XRT_DOXYGEN) 283 /*! 284 * @name Desktop Linux Mainloop Members 285 * @{ 286 */ 287 288 //! Socket that we accept connections on. 289 int listen_socket; 290 291 //! Were we launched by socket activation, instead of explicitly? 292 bool launched_by_socket; 293 294 //! The socket filename we bound to, if any. 295 char *socket_filename; 296 297 /*! @} */ 298 299#define XRT_IPC_GOT_IMPL 300#endif 301 302#if defined(XRT_OS_WINDOWS) || defined(XRT_DOXYGEN) 303 /*! 304 * @name Desktop Windows Mainloop Members 305 * @{ 306 */ 307 308 //! Named Pipe that we accept connections on. 309 HANDLE pipe_handle; 310 311 //! Name of the Pipe that we accept connections on. 312 char *pipe_name; 313 314 /*! @} */ 315 316#define XRT_IPC_GOT_IMPL 317#endif 318 319#ifndef XRT_IPC_GOT_IMPL 320#error "Need port" 321#endif 322}; 323 324/*! 325 * De-initialize the mainloop object. 326 * @public @memberof ipc_server_mainloop 327 */ 328void 329ipc_server_mainloop_deinit(struct ipc_server_mainloop *ml); 330 331/*! 332 * Initialize the mainloop object. 333 * 334 * @return <0 on error. 335 * @public @memberof ipc_server_mainloop 336 */ 337int 338ipc_server_mainloop_init(struct ipc_server_mainloop *ml, bool no_stdin); 339 340/*! 341 * @brief Poll the mainloop. 342 * 343 * Any errors are signalled by calling ipc_server_handle_failure() 344 * @public @memberof ipc_server_mainloop 345 */ 346void 347ipc_server_mainloop_poll(struct ipc_server *vs, struct ipc_server_mainloop *ml); 348 349/*! 350 * Main IPC object for the server. 351 * 352 * @ingroup ipc_server 353 */ 354struct ipc_server 355{ 356 struct xrt_instance *xinst; 357 358 //! Handle for the current process, e.g. pidfile on linux 359 struct u_process *process; 360 361 struct u_debug_gui *debug_gui; 362 363 //! The @ref xrt_iface level system. 364 struct xrt_system *xsys; 365 366 //! System devices. 367 struct xrt_system_devices *xsysd; 368 369 //! Space overseer. 370 struct xrt_space_overseer *xso; 371 372 //! System compositor. 373 struct xrt_system_compositor *xsysc; 374 375 struct ipc_device idevs[XRT_SYSTEM_MAX_DEVICES]; 376 struct xrt_tracking_origin *xtracks[XRT_SYSTEM_MAX_DEVICES]; 377 378 struct ipc_shared_memory *isms[IPC_MAX_CLIENTS]; 379 380 struct ipc_server_mainloop ml; 381 382 // Is the mainloop supposed to run. 383 volatile bool running; 384 385 // Should we exit when a client disconnects. 386 bool exit_on_disconnect; 387 388 // Should we exit when no clients are connected. 389 bool exit_when_idle; 390 391 // Timestamp when last client disconnected (for exit_when_idle delay) 392 uint64_t last_client_disconnect_ns; 393 394 // How long to wait after all clients disconnect before exiting (in nanoseconds) 395 uint64_t exit_when_idle_delay_ns; 396 397 enum u_logging_level log_level; 398 399 struct ipc_thread threads[IPC_MAX_CLIENTS]; 400 401 volatile uint32_t current_slot_index; 402 403 //! Generator for IDs. 404 uint32_t id_generator; 405 406 struct 407 { 408 int active_client_index; 409 int last_active_client_index; 410 411 // Counter for total number of connected clients 412 uint32_t connected_client_count; 413 414 struct os_mutex lock; 415 } global_state; 416 417 /*! 418 * Callbacks for server events. 419 */ 420 const struct ipc_server_callbacks *callbacks; 421 422 /*! 423 * User data passed to callbacks. 424 */ 425 void *callback_data; 426 427 //! Disable listening on stdin for server stop. 428 bool no_stdin; 429}; 430 431/*! 432 * Finish setting up the server by creating the system, compositor and devices. 433 * 434 * @ingroup ipc_server 435 */ 436xrt_result_t 437ipc_server_init_system_if_available_locked(struct ipc_server *s, 438 volatile struct ipc_client_state *ics, 439 bool *out_available); 440 441/*! 442 * Get the current state of a client. 443 * 444 * @ingroup ipc_server 445 */ 446xrt_result_t 447ipc_server_get_client_app_state(struct ipc_server *s, uint32_t client_id, struct ipc_app_state *out_ias); 448 449/*! 450 * Set the new active client. 451 * 452 * @ingroup ipc_server 453 */ 454xrt_result_t 455ipc_server_set_active_client(struct ipc_server *s, uint32_t client_id); 456 457/*! 458 * Toggle the io for this client. 459 * 460 * @ingroup ipc_server 461 */ 462xrt_result_t 463ipc_server_toggle_io_client(struct ipc_server *s, uint32_t client_id); 464 465/*! 466 * Called by client threads to set a session to active. 467 * 468 * @ingroup ipc_server 469 */ 470void 471ipc_server_activate_session(volatile struct ipc_client_state *ics); 472 473/*! 474 * Called by client threads to set a session to deactivate. 475 * 476 * @ingroup ipc_server 477 */ 478void 479ipc_server_deactivate_session(volatile struct ipc_client_state *ics); 480 481/*! 482 * Called by client threads to recalculate active client. 483 * 484 * @ingroup ipc_server 485 */ 486void 487ipc_server_update_state(struct ipc_server *s); 488 489/*! 490 * Thread function for the client side dispatching. 491 * 492 * @ingroup ipc_server 493 */ 494void * 495ipc_server_client_thread(void *_ics); 496 497/*! 498 * This destroys the native compositor for this client and any extra objects 499 * created from it, like all of the swapchains. 500 */ 501void 502ipc_server_client_destroy_session_and_compositor(volatile struct ipc_client_state *ics); 503 504/*! 505 * @defgroup ipc_server_internals Server Internals 506 * @brief These are only called by the platform-specific mainloop polling code. 507 * @ingroup ipc_server 508 * @{ 509 */ 510/*! 511 * Called when a client has connected, it takes the client's ipc handle. 512 * Handles all things needed to be done for a client connecting, like starting 513 * it's thread. 514 * 515 * @param vs The IPC server. 516 * @param ipc_handle Handle to communicate over. 517 * @memberof ipc_server 518 */ 519void 520ipc_server_handle_client_connected(struct ipc_server *vs, xrt_ipc_handle_t ipc_handle); 521 522/*! 523 * Perform whatever needs to be done when the mainloop polling encounters a failure. 524 * @memberof ipc_server 525 */ 526void 527ipc_server_handle_failure(struct ipc_server *vs); 528 529/*! 530 * Perform whatever needs to be done when the mainloop polling identifies that the server should be shut down. 531 * 532 * Does something like setting a flag or otherwise signalling for shutdown: does not itself explicitly exit. 533 * @memberof ipc_server 534 */ 535void 536ipc_server_handle_shutdown_signal(struct ipc_server *vs); 537 538xrt_result_t 539ipc_server_get_system_properties(struct ipc_server *vs, struct xrt_system_properties *out_properties); 540//! @} 541 542/* 543 * 544 * Helpers 545 * 546 */ 547 548/*! 549 * Get a xdev with the given device_id. 550 */ 551static inline struct xrt_device * 552get_xdev(volatile struct ipc_client_state *ics, uint32_t device_id) 553{ 554 return ics->server->idevs[device_id].xdev; 555} 556 557/*! 558 * Get a idev with the given device_id. 559 */ 560static inline struct ipc_device * 561get_idev(volatile struct ipc_client_state *ics, uint32_t device_id) 562{ 563 return &ics->server->idevs[device_id]; 564} 565 566/*! 567 * Get the data in the shared memory of the given client. 568 */ 569static inline struct ipc_shared_memory * 570get_ism(volatile struct ipc_client_state *ics) 571{ 572 return ics->server->isms[ics->server_thread_index]; 573} 574 575/*! 576 * Get the handle for the shared memory of the given client. 577 */ 578static inline xrt_shmem_handle_t 579get_ism_handle(volatile struct ipc_client_state *ics) 580{ 581 return ics->ism_handle; 582} 583 584#ifdef __cplusplus 585} 586#endif