The open source OpenXR runtime
at main 489 lines 18 kB view raw
1// Copyright 2020-2024, Collabora, Ltd. 2// Copyright 2025, NVIDIA CORPORATION. 3// SPDX-License-Identifier: BSL-1.0 4/*! 5 * @file 6 * @brief Shared functions for IPC client @ref xrt_device. 7 * @author Jakob Bornecrantz <jakob@collabora.com> 8 * @author Jakob Bornecrantz <tbornecrantz@nvidia.com> 9 * @author Korcan Hussein <korcan.hussein@collabora.com> 10 * @ingroup ipc_client 11 */ 12 13#include "xrt/xrt_device.h" 14 15#include "os/os_time.h" 16 17#include "math/m_api.h" 18 19#include "util/u_var.h" 20#include "util/u_misc.h" 21#include "util/u_debug.h" 22#include "util/u_device.h" 23 24#include "client/ipc_client.h" 25#include "client/ipc_client_connection.h" 26#include "client/ipc_client_xdev.h" 27#include "ipc_client_generated.h" 28 29 30/* 31 * 32 * Functions from xrt_device. 33 * 34 */ 35 36static xrt_result_t 37ipc_client_xdev_update_inputs(struct xrt_device *xdev) 38{ 39 struct ipc_client_xdev *icx = ipc_client_xdev(xdev); 40 41 xrt_result_t xret = ipc_call_device_update_input(icx->ipc_c, icx->device_id); 42 IPC_CHK_ALWAYS_RET(icx->ipc_c, xret, "ipc_call_device_update_input"); 43} 44 45static xrt_result_t 46ipc_client_xdev_get_tracked_pose(struct xrt_device *xdev, 47 enum xrt_input_name name, 48 int64_t at_timestamp_ns, 49 struct xrt_space_relation *out_relation) 50{ 51 struct ipc_client_xdev *icx = ipc_client_xdev(xdev); 52 53 xrt_result_t xret = ipc_call_device_get_tracked_pose( // 54 icx->ipc_c, // 55 icx->device_id, // 56 name, // 57 at_timestamp_ns, // 58 out_relation); // 59 IPC_CHK_ALWAYS_RET(icx->ipc_c, xret, "ipc_call_device_get_tracked_pose"); 60} 61 62static xrt_result_t 63ipc_client_xdev_get_hand_tracking(struct xrt_device *xdev, 64 enum xrt_input_name name, 65 int64_t at_timestamp_ns, 66 struct xrt_hand_joint_set *out_value, 67 int64_t *out_timestamp_ns) 68{ 69 struct ipc_client_xdev *icx = ipc_client_xdev(xdev); 70 71 xrt_result_t xret = ipc_call_device_get_hand_tracking( // 72 icx->ipc_c, // 73 icx->device_id, // 74 name, // 75 at_timestamp_ns, // 76 out_value, // 77 out_timestamp_ns); // 78 IPC_CHK_ALWAYS_RET(icx->ipc_c, xret, "ipc_call_device_get_hand_tracking"); 79} 80 81static xrt_result_t 82ipc_client_xdev_get_face_tracking(struct xrt_device *xdev, 83 enum xrt_input_name facial_expression_type, 84 int64_t at_timestamp_ns, 85 struct xrt_facial_expression_set *out_value) 86{ 87 struct ipc_client_xdev *icx = ipc_client_xdev(xdev); 88 89 xrt_result_t xret = ipc_call_device_get_face_tracking( // 90 icx->ipc_c, // 91 icx->device_id, // 92 facial_expression_type, // 93 at_timestamp_ns, // 94 out_value); // 95 IPC_CHK_ALWAYS_RET(icx->ipc_c, xret, "ipc_call_device_get_face_tracking"); 96} 97 98static xrt_result_t 99ipc_client_xdev_get_body_skeleton(struct xrt_device *xdev, 100 enum xrt_input_name body_tracking_type, 101 struct xrt_body_skeleton *out_value) 102{ 103 struct ipc_client_xdev *icx = ipc_client_xdev(xdev); 104 105 xrt_result_t xret = ipc_call_device_get_body_skeleton( // 106 icx->ipc_c, // 107 icx->device_id, // 108 body_tracking_type, // 109 out_value); // 110 IPC_CHK_ALWAYS_RET(icx->ipc_c, xret, "ipc_call_device_get_body_skeleton"); 111} 112 113static xrt_result_t 114ipc_client_xdev_get_body_joints(struct xrt_device *xdev, 115 enum xrt_input_name body_tracking_type, 116 int64_t desired_timestamp_ns, 117 struct xrt_body_joint_set *out_value) 118{ 119 struct ipc_client_xdev *icx = ipc_client_xdev(xdev); 120 121 xrt_result_t xret = ipc_call_device_get_body_joints( // 122 icx->ipc_c, // 123 icx->device_id, // 124 body_tracking_type, // 125 desired_timestamp_ns, // 126 out_value); // 127 IPC_CHK_ALWAYS_RET(icx->ipc_c, xret, "ipc_call_device_get_body_joints"); 128} 129 130static xrt_result_t 131ipc_client_xdev_reset_body_tracking_calibration_meta(struct xrt_device *xdev) 132{ 133 struct ipc_client_xdev *icx = ipc_client_xdev(xdev); 134 135 xrt_result_t xret = ipc_call_device_reset_body_tracking_calibration_meta( // 136 icx->ipc_c, // 137 icx->device_id); // 138 IPC_CHK_ALWAYS_RET(icx->ipc_c, xret, "ipc_call_device_reset_body_tracking_calibration_meta"); 139} 140 141static xrt_result_t 142ipc_client_xdev_set_body_tracking_calibration_override_meta(struct xrt_device *xdev, float new_body_height) 143{ 144 struct ipc_client_xdev *icx = ipc_client_xdev(xdev); 145 146 xrt_result_t xret = ipc_call_device_set_body_tracking_calibration_override_meta( // 147 icx->ipc_c, // 148 icx->device_id, // 149 new_body_height); // 150 IPC_CHK_ALWAYS_RET(icx->ipc_c, xret, "ipc_call_device_set_body_tracking_calibration_override_meta"); 151} 152 153static xrt_result_t 154ipc_client_xdev_get_presence(struct xrt_device *xdev, bool *presence) 155{ 156 struct ipc_client_xdev *icx = ipc_client_xdev(xdev); 157 158 xrt_result_t xret = ipc_call_device_get_presence( // 159 icx->ipc_c, // 160 icx->device_id, // 161 presence); // 162 IPC_CHK_ALWAYS_RET(icx->ipc_c, xret, "ipc_call_device_get_presence"); 163} 164 165static xrt_result_t 166ipc_client_xdev_set_output(struct xrt_device *xdev, enum xrt_output_name name, const struct xrt_output_value *value) 167{ 168 struct ipc_client_xdev *icx = ipc_client_xdev(xdev); 169 struct ipc_connection *ipc_c = icx->ipc_c; 170 171 xrt_result_t xret; 172 if (value->type == XRT_OUTPUT_VALUE_TYPE_PCM_VIBRATION) { 173 uint32_t samples_sent = MIN(value->pcm_vibration.sample_rate, 4000); 174 175 struct ipc_pcm_haptic_buffer samples = { 176 .append = value->pcm_vibration.append, 177 .num_samples = samples_sent, 178 .sample_rate = value->pcm_vibration.sample_rate, 179 }; 180 181 ipc_client_connection_lock(ipc_c); 182 183 xret = ipc_send_device_set_haptic_output_locked(ipc_c, icx->device_id, name, &samples); 184 IPC_CHK_WITH_GOTO(ipc_c, xret, "ipc_send_device_set_haptic_output_locked", send_haptic_output_end); 185 186 xrt_result_t alloc_xret; 187 xret = ipc_receive(&ipc_c->imc, &alloc_xret, sizeof alloc_xret); 188 if (xret != XRT_SUCCESS || alloc_xret != XRT_SUCCESS) { 189 goto send_haptic_output_end; 190 } 191 192 xret = ipc_send(&ipc_c->imc, value->pcm_vibration.buffer, sizeof(float) * samples_sent); 193 if (xret != XRT_SUCCESS) { 194 goto send_haptic_output_end; 195 } 196 197 xret = ipc_receive(&ipc_c->imc, value->pcm_vibration.samples_consumed, 198 sizeof(*value->pcm_vibration.samples_consumed)); 199 if (xret != XRT_SUCCESS) { 200 goto send_haptic_output_end; 201 } 202 203 send_haptic_output_end: 204 ipc_client_connection_unlock(ipc_c); 205 } else { 206 xret = ipc_call_device_set_output(ipc_c, icx->device_id, name, value); 207 IPC_CHK_ONLY_PRINT(ipc_c, xret, "ipc_call_device_set_output"); 208 } 209 210 return xret; 211} 212 213xrt_result_t 214ipc_client_xdev_get_output_limits(struct xrt_device *xdev, struct xrt_output_limits *limits) 215{ 216 struct ipc_client_xdev *icx = ipc_client_xdev(xdev); 217 218 xrt_result_t xret = ipc_call_device_get_output_limits(icx->ipc_c, icx->device_id, limits); 219 IPC_CHK_ONLY_PRINT(icx->ipc_c, xret, "ipc_call_device_get_output_limits"); 220 221 return xret; 222} 223 224 225/* 226 * 227 * Plane detection functions. 228 * 229 */ 230 231static xrt_result_t 232ipc_client_xdev_begin_plane_detection_ext(struct xrt_device *xdev, 233 const struct xrt_plane_detector_begin_info_ext *begin_info, 234 uint64_t plane_detection_id, 235 uint64_t *out_plane_detection_id) 236{ 237 struct ipc_client_xdev *icx = ipc_client_xdev(xdev); 238 239 icx->ipc_c->ism->plane_begin_info_ext = *begin_info; 240 241 xrt_result_t r = ipc_call_device_begin_plane_detection_ext(icx->ipc_c, icx->device_id, plane_detection_id, 242 out_plane_detection_id); 243 if (r != XRT_SUCCESS) { 244 IPC_ERROR(icx->ipc_c, "Error sending hmd_begin_plane_detection_ext!"); 245 return r; 246 } 247 248 return XRT_SUCCESS; 249} 250 251static xrt_result_t 252ipc_client_xdev_destroy_plane_detection_ext(struct xrt_device *xdev, uint64_t plane_detection_id) 253{ 254 struct ipc_client_xdev *icx = ipc_client_xdev(xdev); 255 256 xrt_result_t r = ipc_call_device_destroy_plane_detection_ext(icx->ipc_c, icx->device_id, plane_detection_id); 257 if (r != XRT_SUCCESS) { 258 IPC_ERROR(icx->ipc_c, "Error sending destroy_plane_detection_ext!"); 259 return r; 260 } 261 262 return XRT_SUCCESS; 263} 264 265/*! 266 * Helper function for @ref xrt_device::get_plane_detection_state. 267 * 268 * @public @memberof xrt_device 269 */ 270static xrt_result_t 271ipc_client_xdev_get_plane_detection_state_ext(struct xrt_device *xdev, 272 uint64_t plane_detection_id, 273 enum xrt_plane_detector_state_ext *out_state) 274{ 275 struct ipc_client_xdev *icx = ipc_client_xdev(xdev); 276 277 xrt_result_t r = 278 ipc_call_device_get_plane_detection_state_ext(icx->ipc_c, icx->device_id, plane_detection_id, out_state); 279 if (r != XRT_SUCCESS) { 280 IPC_ERROR(icx->ipc_c, "Error sending get_plane_detection_state_ext!"); 281 return r; 282 } 283 284 return XRT_SUCCESS; 285} 286 287/*! 288 * Helper function for @ref xrt_device::get_plane_detections. 289 * 290 * @public @memberof xrt_device 291 */ 292static xrt_result_t 293ipc_client_xdev_get_plane_detections_ext(struct xrt_device *xdev, 294 uint64_t plane_detection_id, 295 struct xrt_plane_detections_ext *out_detections) 296{ 297 struct ipc_client_xdev *icx = ipc_client_xdev(xdev); 298 struct ipc_connection *ipc_c = icx->ipc_c; 299 300 ipc_client_connection_lock(ipc_c); 301 302 xrt_result_t xret = ipc_send_device_get_plane_detections_ext_locked(ipc_c, icx->device_id, plane_detection_id); 303 IPC_CHK_WITH_GOTO(icx->ipc_c, xret, "ipc_send_device_get_plane_detections_ext_locked", out); 304 305 // in this case, size == count 306 uint32_t location_size = 0; 307 uint32_t polygon_size = 0; 308 uint32_t vertex_size = 0; 309 310 xret = ipc_receive_device_get_plane_detections_ext_locked(ipc_c, &location_size, &polygon_size, &vertex_size); 311 IPC_CHK_WITH_GOTO(icx->ipc_c, xret, "ipc_receive_device_get_plane_detections_ext_locked", out); 312 313 314 // With no locations, the service won't send anything else 315 if (location_size < 1) { 316 out_detections->location_count = 0; 317 goto out; 318 } 319 320 // realloc arrays in out_detections if necessary, then receive contents 321 322 out_detections->location_count = location_size; 323 if (out_detections->location_size < location_size) { 324 U_ARRAY_REALLOC_OR_FREE(out_detections->locations, struct xrt_plane_detector_location_ext, 325 location_size); 326 U_ARRAY_REALLOC_OR_FREE(out_detections->polygon_info_start_index, uint32_t, location_size); 327 out_detections->location_size = location_size; 328 } 329 330 if (out_detections->polygon_info_size < polygon_size) { 331 U_ARRAY_REALLOC_OR_FREE(out_detections->polygon_infos, struct xrt_plane_polygon_info_ext, polygon_size); 332 out_detections->polygon_info_size = polygon_size; 333 } 334 335 if (out_detections->vertex_size < vertex_size) { 336 U_ARRAY_REALLOC_OR_FREE(out_detections->vertices, struct xrt_vec2, vertex_size); 337 out_detections->vertex_size = vertex_size; 338 } 339 340 if ((location_size > 0 && 341 (out_detections->locations == NULL || out_detections->polygon_info_start_index == NULL)) || 342 (polygon_size > 0 && out_detections->polygon_infos == NULL) || 343 (vertex_size > 0 && out_detections->vertices == NULL)) { 344 IPC_ERROR(icx->ipc_c, "Error allocating memory for plane detections!"); 345 out_detections->location_size = 0; 346 out_detections->polygon_info_size = 0; 347 out_detections->vertex_size = 0; 348 xret = XRT_ERROR_IPC_FAILURE; 349 goto out; 350 } 351 352 if (location_size > 0) { 353 // receive location_count * locations 354 xret = ipc_receive(&ipc_c->imc, out_detections->locations, 355 sizeof(struct xrt_plane_detector_location_ext) * location_size); 356 IPC_CHK_WITH_GOTO(icx->ipc_c, xret, "ipc_receive(1)", out); 357 358 // receive location_count * polygon_info_start_index 359 xret = ipc_receive(&ipc_c->imc, out_detections->polygon_info_start_index, 360 sizeof(uint32_t) * location_size); 361 IPC_CHK_WITH_GOTO(icx->ipc_c, xret, "ipc_receive(2)", out); 362 } 363 364 365 if (polygon_size > 0) { 366 // receive polygon_count * polygon_infos 367 xret = ipc_receive(&ipc_c->imc, out_detections->polygon_infos, 368 sizeof(struct xrt_plane_polygon_info_ext) * polygon_size); 369 IPC_CHK_WITH_GOTO(icx->ipc_c, xret, "ipc_receive(3)", out); 370 } 371 372 if (vertex_size > 0) { 373 // receive vertex_count * vertices 374 xret = ipc_receive(&ipc_c->imc, out_detections->vertices, sizeof(struct xrt_vec2) * vertex_size); 375 IPC_CHK_WITH_GOTO(icx->ipc_c, xret, "ipc_receive(4)", out); 376 } 377 378out: 379 ipc_client_connection_unlock(ipc_c); 380 return xret; 381} 382 383 384/* 385 * 386 * 'Exported' functions. 387 * 388 */ 389 390void 391ipc_client_xdev_init(struct ipc_client_xdev *icx, 392 struct ipc_connection *ipc_c, 393 struct xrt_tracking_origin *xtrack, 394 uint32_t device_id, 395 u_device_destroy_function_t destroy_fn) 396{ 397 // Helpers. 398 struct ipc_shared_memory *ism = ipc_c->ism; 399 struct ipc_shared_device *isdev = &ism->isdevs[device_id]; 400 401 // Important fields. 402 icx->ipc_c = ipc_c; 403 icx->device_id = device_id; 404 405 /* 406 * Fill in not implemented or noop versions first, 407 * destroy gets filled in by either device or HMD. 408 */ 409 u_device_populate_function_pointers(&icx->base, ipc_client_xdev_get_tracked_pose, destroy_fn); 410 411 // Shared implemented functions. 412 icx->base.update_inputs = ipc_client_xdev_update_inputs; 413 icx->base.get_hand_tracking = ipc_client_xdev_get_hand_tracking; 414 icx->base.get_face_tracking = ipc_client_xdev_get_face_tracking; 415 icx->base.get_body_skeleton = ipc_client_xdev_get_body_skeleton; 416 icx->base.get_body_joints = ipc_client_xdev_get_body_joints; 417 icx->base.reset_body_tracking_calibration_meta = ipc_client_xdev_reset_body_tracking_calibration_meta; 418 icx->base.set_body_tracking_calibration_override_meta = 419 ipc_client_xdev_set_body_tracking_calibration_override_meta; 420 icx->base.get_presence = ipc_client_xdev_get_presence; 421 icx->base.set_output = ipc_client_xdev_set_output; 422 icx->base.get_output_limits = ipc_client_xdev_get_output_limits; 423 424 // Plane detection EXT. 425 icx->base.begin_plane_detection_ext = ipc_client_xdev_begin_plane_detection_ext; 426 icx->base.destroy_plane_detection_ext = ipc_client_xdev_destroy_plane_detection_ext; 427 icx->base.get_plane_detection_state_ext = ipc_client_xdev_get_plane_detection_state_ext; 428 icx->base.get_plane_detections_ext = ipc_client_xdev_get_plane_detections_ext; 429 430 // Copying the information from the isdev. 431 icx->base.device_type = isdev->device_type; 432 icx->base.supported = isdev->supported; 433 icx->base.tracking_origin = xtrack; 434 icx->base.name = isdev->name; 435 436 // Print name. 437 snprintf(icx->base.str, XRT_DEVICE_NAME_LEN, "%s", isdev->str); 438 snprintf(icx->base.serial, XRT_DEVICE_NAME_LEN, "%s", isdev->serial); 439 440 // Setup inputs, by pointing directly to the shared memory. 441 assert(isdev->input_count > 0); 442 icx->base.inputs = &ism->inputs[isdev->first_input_index]; 443 icx->base.input_count = isdev->input_count; 444 445 // Setup outputs, if any point directly into the shared memory. 446 icx->base.output_count = isdev->output_count; 447 if (isdev->output_count > 0) { 448 icx->base.outputs = &ism->outputs[isdev->first_output_index]; 449 } else { 450 icx->base.outputs = NULL; 451 } 452 453 // Setup binding profiles. 454 icx->base.binding_profile_count = isdev->binding_profile_count; 455 if (isdev->binding_profile_count > 0) { 456 icx->base.binding_profiles = 457 U_TYPED_ARRAY_CALLOC(struct xrt_binding_profile, isdev->binding_profile_count); 458 } 459 460 for (size_t i = 0; i < isdev->binding_profile_count; i++) { 461 struct xrt_binding_profile *xbp = &icx->base.binding_profiles[i]; 462 struct ipc_shared_binding_profile *isbp = 463 &ism->binding_profiles[isdev->first_binding_profile_index + i]; 464 465 xbp->name = isbp->name; 466 if (isbp->input_count > 0) { 467 xbp->inputs = &ism->input_pairs[isbp->first_input_index]; 468 xbp->input_count = isbp->input_count; 469 } 470 if (isbp->output_count > 0) { 471 xbp->outputs = &ism->output_pairs[isbp->first_output_index]; 472 xbp->output_count = isbp->output_count; 473 } 474 } 475} 476 477void 478ipc_client_xdev_fini(struct ipc_client_xdev *icx) 479{ 480 // We do not own these, so don't free them. 481 icx->base.inputs = NULL; 482 icx->base.outputs = NULL; 483 484 // We allocated the bindings profiles. 485 if (icx->base.binding_profiles != NULL) { 486 free(icx->base.binding_profiles); 487 icx->base.binding_profiles = NULL; 488 } 489}