diff --git a/src/xrt/include/xrt/xrt_instance.h b/src/xrt/include/xrt/xrt_instance.h index 1e10d6204..8a2d1dcaa 100644 --- a/src/xrt/include/xrt/xrt_instance.h +++ b/src/xrt/include/xrt/xrt_instance.h @@ -1,4 +1,5 @@ // Copyright 2020-2024, Collabora, Ltd. +// Copyright 2025, NVIDIA CORPORATION. // SPDX-License-Identifier: BSL-1.0 /*! * @file @@ -125,6 +126,11 @@ struct xrt_instance * @{ */ + /*! + * Checks if the system can be created with create_system(). + */ + xrt_result_t (*is_system_available)(struct xrt_instance *xinst, bool *out_available); + /*! * Creates all of the system resources like the devices and system * compositor. The system compositor is optional. @@ -199,6 +205,20 @@ struct xrt_instance struct xrt_instance_android *android_instance; }; + +/*! + * @copydoc xrt_instance::create_system + * + * Helper for calling through the function pointer. + * + * @public @memberof xrt_instance + */ +static inline xrt_result_t +xrt_instance_is_system_available(struct xrt_instance *xinst, bool *out_available) +{ + return xinst->is_system_available(xinst, out_available); +} + /*! * @copydoc xrt_instance::create_system * diff --git a/src/xrt/ipc/client/ipc_client_instance.c b/src/xrt/ipc/client/ipc_client_instance.c index 47f883288..6020f5d5b 100644 --- a/src/xrt/ipc/client/ipc_client_instance.c +++ b/src/xrt/ipc/client/ipc_client_instance.c @@ -130,6 +130,14 @@ err_xina: * */ +static xrt_result_t +ipc_client_instance_is_system_available(struct xrt_instance *xinst, bool *out_available) +{ + struct ipc_client_instance *ii = ipc_client_instance(xinst); + xrt_result_t xret = ipc_call_instance_is_system_available(&ii->ipc_c, out_available); + IPC_CHK_ALWAYS_RET(&ii->ipc_c, xret, "ipc_call_instance_is_system_available"); +} + static xrt_result_t ipc_client_instance_create_system(struct xrt_instance *xinst, struct xrt_system **out_xsys, @@ -284,6 +292,7 @@ xrt_result_t ipc_instance_create(const struct xrt_instance_info *i_info, struct xrt_instance **out_xinst) { struct ipc_client_instance *ii = U_TYPED_CALLOC(struct ipc_client_instance); + ii->base.is_system_available = ipc_client_instance_is_system_available; ii->base.create_system = ipc_client_instance_create_system; ii->base.get_prober = ipc_client_instance_get_prober; ii->base.destroy = ipc_client_instance_destroy; diff --git a/src/xrt/ipc/server/ipc_server.h b/src/xrt/ipc/server/ipc_server.h index 40357013b..1dceaa95b 100644 --- a/src/xrt/ipc/server/ipc_server.h +++ b/src/xrt/ipc/server/ipc_server.h @@ -1,4 +1,5 @@ // Copyright 2020-2023, Collabora, Ltd. +// Copyright 2025, NVIDIA CORPORATION. // SPDX-License-Identifier: BSL-1.0 /*! * @file @@ -90,6 +91,9 @@ struct ipc_client_state //! Link back to the main server. struct ipc_server *server; + //! Has the system part of the shm initialized. + bool has_init_shm_system; + //! Session for this client. struct xrt_session *xs; @@ -409,6 +413,15 @@ struct ipc_server } global_state; }; +/*! + * Finish setting up the server by creating the system, compositor and devices. + * + * @ingroup ipc_server + */ +xrt_result_t +ipc_server_init_system_if_available_locked(struct ipc_server *s, + volatile struct ipc_client_state *ics, + bool *out_available); /*! * Get the current state of a client. diff --git a/src/xrt/ipc/server/ipc_server_handler.c b/src/xrt/ipc/server/ipc_server_handler.c index 47ce3a709..a24cca5d2 100644 --- a/src/xrt/ipc/server/ipc_server_handler.c +++ b/src/xrt/ipc/server/ipc_server_handler.c @@ -399,6 +399,25 @@ ipc_handle_instance_describe_client(volatile struct ipc_client_state *ics, return XRT_SUCCESS; } +xrt_result_t +ipc_handle_instance_is_system_available(volatile struct ipc_client_state *ics, bool *out_available) +{ + IPC_TRACE_MARKER(); + + xrt_result_t xret = XRT_SUCCESS; + + struct ipc_server *s = ics->server; + + os_mutex_lock(&s->global_state.lock); + + xret = ipc_server_init_system_if_available_locked(s, ics, out_available); + IPC_CHK_WITH_GOTO(s, xret, "ipc_server_init_system_if_available_locked", cleanup); + +cleanup: + os_mutex_unlock(&s->global_state.lock); + return xret; +} + xrt_result_t ipc_handle_system_compositor_get_info(volatile struct ipc_client_state *ics, struct xrt_system_compositor_info *out_info) diff --git a/src/xrt/ipc/server/ipc_server_process.c b/src/xrt/ipc/server/ipc_server_process.c index c38e991ea..96eadd01d 100644 --- a/src/xrt/ipc/server/ipc_server_process.c +++ b/src/xrt/ipc/server/ipc_server_process.c @@ -279,18 +279,32 @@ handle_binding(struct ipc_shared_memory *ism, } XRT_CHECK_RESULT static xrt_result_t -init_shm(struct ipc_server *s, volatile struct ipc_client_state *cs) +init_shm_and_instance_state(struct ipc_server *s, volatile struct ipc_client_state *ics) { const size_t size = sizeof(struct ipc_shared_memory); xrt_shmem_handle_t handle; - xrt_result_t xret = ipc_shmem_create(size, &handle, (void **)&s->isms[cs->server_thread_index]); + xrt_result_t xret = ipc_shmem_create(size, &handle, (void **)&s->isms[ics->server_thread_index]); IPC_CHK_AND_RET(s, xret, "ipc_shmem_create"); // we have a filehandle, we will pass this to our client - cs->ism_handle = handle; + ics->ism_handle = handle; + // Convenience + struct ipc_shared_memory *ism = s->isms[ics->server_thread_index]; + // Clients expect git version info and timestamp available upon connect. + snprintf(ism->u_git_tag, IPC_VERSION_NAME_LEN, "%s", u_git_tag); + + // Used to synchronize all client's xrt_instance::startup_timestamp. + ism->startup_timestamp = os_monotonic_get_ns(); + + return XRT_SUCCESS; +} + +static void +init_system_shm_state(struct ipc_server *s, volatile struct ipc_client_state *cs) +{ /* * * Setup the shared memory state. @@ -300,8 +314,6 @@ init_shm(struct ipc_server *s, volatile struct ipc_client_state *cs) uint32_t count = 0; struct ipc_shared_memory *ism = s->isms[cs->server_thread_index]; - ism->startup_timestamp = os_monotonic_get_ns(); - // Setup the tracking origins. count = 0; for (size_t i = 0; i < XRT_SYSTEM_MAX_DEVICES; i++) { @@ -423,17 +435,13 @@ init_shm(struct ipc_server *s, volatile struct ipc_client_state *cs) ism->roles.eyes = find_xdev_index(s, s->xsysd->static_roles.eyes); ism->roles.face = find_xdev_index(s, s->xsysd->static_roles.face); ism->roles.body = find_xdev_index(s, s->xsysd->static_roles.body); + #define SET_HT_ROLE(SRC) \ ism->roles.hand_tracking.SRC.left = find_xdev_index(s, s->xsysd->static_roles.hand_tracking.SRC.left); \ ism->roles.hand_tracking.SRC.right = find_xdev_index(s, s->xsysd->static_roles.hand_tracking.SRC.right); SET_HT_ROLE(unobstructed) SET_HT_ROLE(conforming) #undef SET_HT_ROLE - - // Fill out git version info. - snprintf(ism->u_git_tag, IPC_VERSION_NAME_LEN, "%s", u_git_tag); - - return XRT_SUCCESS; } static void @@ -488,13 +496,6 @@ init_all(struct ipc_server *s, enum u_logging_level log_level, bool exit_on_disc xret = xrt_instance_create(NULL, &s->xinst); IPC_CHK_WITH_GOTO(s, xret, "xrt_instance_create", error); - xret = xrt_instance_create_system(s->xinst, &s->xsys, &s->xsysd, &s->xso, &s->xsysc); - IPC_CHK_WITH_GOTO(s, xret, "xrt_instance_create_system", error); - - // Always succeeds. - init_idevs(s); - init_tracking_origins(s); - ret = ipc_server_mainloop_init(&s->ml); if (ret < 0) { xret = XRT_ERROR_IPC_MAINLOOP_FAILED_TO_INIT; @@ -805,6 +806,46 @@ allocate_id_locked(struct ipc_server *s) * */ +xrt_result_t +ipc_server_init_system_if_available_locked(struct ipc_server *s, + volatile struct ipc_client_state *ics, + bool *out_available) +{ + xrt_result_t xret = XRT_SUCCESS; + + bool available = false; + + if (s->xsys) { + available = true; + } else { + xret = xrt_instance_is_system_available(s->xinst, &available); + IPC_CHK_WITH_GOTO(s, xret, "xrt_instance_is_system_available", error); + + if (available) { + xret = xrt_instance_create_system(s->xinst, &s->xsys, &s->xsysd, &s->xso, &s->xsysc); + IPC_CHK_WITH_GOTO(s, xret, "xrt_instance_create_system", error); + + // Always succeeds. + init_idevs(s); + init_tracking_origins(s); + } + } + + if (available && ics != NULL && !ics->has_init_shm_system) { + init_system_shm_state(s, ics); + ics->has_init_shm_system = true; + } + + if (out_available) { + *out_available = available; + } + + return XRT_SUCCESS; + +error: + return xret; +} + xrt_result_t ipc_server_get_client_app_state(struct ipc_server *s, uint32_t client_id, struct ipc_app_state *out_ias) { @@ -982,7 +1023,7 @@ ipc_server_handle_client_connected(struct ipc_server *vs, xrt_ipc_handle_t ipc_h ics->plane_detection_ids = NULL; ics->plane_detection_xdev = NULL; - xrt_result_t xret = init_shm(vs, ics); + xrt_result_t xret = init_shm_and_instance_state(vs, ics); if (xret != XRT_SUCCESS) { // Unlock when we are done. @@ -1049,6 +1090,15 @@ ipc_server_main_common(const struct ipc_server_main_info *ismi, // Tell the callbacks we are entering the main-loop. callbacks->mainloop_entering(s, s->xinst, data); + // Early init the system. If not available now, will try again per client request. + xret = ipc_server_init_system_if_available_locked( // + s, // + NULL, // optional - ics + NULL); // optional - out_available + if (xret != XRT_SUCCESS) { + U_LOG_CHK_ONLY_PRINT(log_level, xret, "ipc_server_init_system_if_available_locked"); + } + // Main loop. ret = main_loop(s); diff --git a/src/xrt/ipc/shared/proto.json b/src/xrt/ipc/shared/proto.json index 5b8cb3d52..5c1e047d8 100644 --- a/src/xrt/ipc/shared/proto.json +++ b/src/xrt/ipc/shared/proto.json @@ -11,6 +11,12 @@ ] }, + "instance_is_system_available": { + "out": [ + {"name": "available", "type": "bool"} + ] + }, + "system_get_properties": { "out": [ {"name": "properties", "type": "struct xrt_system_properties"} diff --git a/src/xrt/state_trackers/oxr/oxr_instance.c b/src/xrt/state_trackers/oxr/oxr_instance.c index 4c3a7fb52..944fcae90 100644 --- a/src/xrt/state_trackers/oxr/oxr_instance.c +++ b/src/xrt/state_trackers/oxr/oxr_instance.c @@ -479,6 +479,21 @@ oxr_instance_init_system_locked(struct oxr_logger *log, struct oxr_instance *ins xrt_result_t xret; XrResult ret; + bool available = false; + xret = xrt_instance_is_system_available(inst->xinst, &available); + if (xret != XRT_SUCCESS) { + struct u_pp_sink_stack_only sink; + u_pp_delegate_t dg = u_pp_sink_stack_only_init(&sink); + u_pp(dg, "Call to xrt_instance_is_system_available failed: "); + u_pp_xrt_result(dg, xret); + ret = oxr_error(log, xret == XRT_ERROR_IPC_FAILURE ? XR_ERROR_INSTANCE_LOST : XR_ERROR_RUNTIME_FAILURE, + "%s", sink.buffer); + return ret; + } + if (!available) { + return XR_ERROR_FORM_FACTOR_UNAVAILABLE; + } + // Create the compositor if we are not headless, currently always create it. bool should_create_compositor = true /* !inst->extensions.MND_headless */; diff --git a/src/xrt/targets/common/target_instance.c b/src/xrt/targets/common/target_instance.c index e10ba8358..b0492d689 100644 --- a/src/xrt/targets/common/target_instance.c +++ b/src/xrt/targets/common/target_instance.c @@ -1,4 +1,5 @@ // Copyright 2020-2024, Collabora, Ltd. +// Copyright 2025, NVIDIA CORPORATION. // SPDX-License-Identifier: BSL-1.0 /*! * @file @@ -51,6 +52,18 @@ null_compositor_create_system(struct xrt_device *xdev, struct xrt_system_composi * */ +static xrt_result_t +t_instance_is_system_available(struct xrt_instance *xinst, bool *out_available) +{ + XRT_TRACE_MARKER(); + + assert(out_available != NULL); + + *out_available = true; + + return XRT_SUCCESS; +} + static xrt_result_t t_instance_create_system(struct xrt_instance *xinst, struct xrt_system **out_xsys, @@ -168,6 +181,7 @@ xrt_instance_create(struct xrt_instance_info *ii, struct xrt_instance **out_xins } struct t_instance *tinst = U_TYPED_CALLOC(struct t_instance); + tinst->base.is_system_available = t_instance_is_system_available; tinst->base.create_system = t_instance_create_system; tinst->base.get_prober = t_instance_get_prober; tinst->base.destroy = t_instance_destroy;