The open source OpenXR runtime
at main 355 lines 11 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 IPC Client HMD device. 7 * @author Jakob Bornecrantz <jakob@collabora.com> 8 * @author Korcan Hussein <korcan.hussein@collabora.com> 9 * @ingroup ipc_client 10 */ 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#include "util/u_distortion_mesh.h" 24 25#include "client/ipc_client.h" 26#include "client/ipc_client_xdev.h" 27#include "client/ipc_client_connection.h" 28#include "ipc_client_generated.h" 29 30#include <math.h> 31#include <stdio.h> 32#include <stdlib.h> 33#include <string.h> 34#include <assert.h> 35 36 37/* 38 * 39 * Structs and defines. 40 * 41 */ 42 43/*! 44 * An IPC client proxy for an HMD @ref xrt_device and @ref ipc_client_xdev. 45 * Using a typedef reduce impact of refactor change. 46 * 47 * @implements ipc_client_xdev 48 * @ingroup ipc_client 49 */ 50typedef struct ipc_client_xdev ipc_client_hmd_t; 51 52 53/* 54 * 55 * Helpers. 56 * 57 */ 58 59static inline ipc_client_hmd_t * 60ipc_client_hmd(struct xrt_device *xdev) 61{ 62 return (ipc_client_hmd_t *)xdev; 63} 64 65static xrt_result_t 66call_get_view_poses_raw(ipc_client_hmd_t *ich, 67 const struct xrt_vec3 *default_eye_relation, 68 int64_t at_timestamp_ns, 69 enum xrt_view_type view_type, 70 uint32_t view_count, 71 struct xrt_space_relation *out_head_relation, 72 struct xrt_fov *out_fovs, 73 struct xrt_pose *out_poses) 74{ 75 struct ipc_connection *ipc_c = ich->ipc_c; 76 xrt_result_t xret; 77 78 ipc_client_connection_lock(ipc_c); 79 80 // Using the raw send helper is the only one that is required. 81 xret = ipc_send_device_get_view_poses_locked( // 82 ipc_c, // 83 ich->device_id, // 84 default_eye_relation, // 85 at_timestamp_ns, // 86 view_type, // 87 view_count); // 88 IPC_CHK_WITH_GOTO(ich->ipc_c, xret, "ipc_send_device_get_view_poses_locked", out); 89 90 // This is the data we get back in the provided reply. 91 uint32_t returned_view_count = 0; 92 struct xrt_space_relation head_relation = XRT_SPACE_RELATION_ZERO; 93 94 // Get the reply, use the raw function helper. 95 xret = ipc_receive_device_get_view_poses_locked( // 96 ipc_c, // 97 &head_relation, // 98 &returned_view_count); // 99 IPC_CHK_WITH_GOTO(ich->ipc_c, xret, "ipc_receive_device_get_view_poses_locked", out); 100 101 if (view_count != returned_view_count) { 102 IPC_ERROR(ich->ipc_c, "Wrong view counts (sent: %u != got: %u)", view_count, returned_view_count); 103 assert(false); 104 } 105 106 // We can read directly to the output variables. 107 xret = ipc_receive(&ipc_c->imc, out_fovs, sizeof(struct xrt_fov) * view_count); 108 IPC_CHK_WITH_GOTO(ich->ipc_c, xret, "ipc_receive(1)", out); 109 110 // We can read directly to the output variables. 111 xret = ipc_receive(&ipc_c->imc, out_poses, sizeof(struct xrt_pose) * view_count); 112 IPC_CHK_WITH_GOTO(ich->ipc_c, xret, "ipc_receive(2)", out); 113 114 /* 115 * Finally set the head_relation that we got in the reply, mostly to 116 * demonstrate that you can use the reply struct in such a way. 117 */ 118 *out_head_relation = head_relation; 119 120out: 121 ipc_client_connection_unlock(ipc_c); 122 return xret; 123} 124 125 126/* 127 * 128 * Member functions 129 * 130 */ 131 132static xrt_result_t 133ipc_client_hmd_get_view_poses(struct xrt_device *xdev, 134 const struct xrt_vec3 *default_eye_relation, 135 int64_t at_timestamp_ns, 136 enum xrt_view_type view_type, 137 uint32_t view_count, 138 struct xrt_space_relation *out_head_relation, 139 struct xrt_fov *out_fovs, 140 struct xrt_pose *out_poses) 141{ 142 ipc_client_hmd_t *ich = ipc_client_hmd(xdev); 143 xrt_result_t xret; 144 145 struct ipc_info_get_view_poses_2 info = {0}; 146 147 if (view_count == 2) { 148 // Fast path. 149 xret = ipc_call_device_get_view_poses_2( // 150 ich->ipc_c, // 151 ich->device_id, // 152 default_eye_relation, // 153 at_timestamp_ns, // 154 view_type, // 155 view_count, // 156 &info); // 157 IPC_CHK_AND_RET(ich->ipc_c, xret, "ipc_call_device_get_view_poses_2"); 158 159 *out_head_relation = info.head_relation; 160 for (int i = 0; i < 2; i++) { 161 out_fovs[i] = info.fovs[i]; 162 out_poses[i] = info.poses[i]; 163 } 164 165 } else if (view_count <= IPC_MAX_RAW_VIEWS) { 166 // Artificial limit. 167 xret = call_get_view_poses_raw( // 168 ich, // 169 default_eye_relation, // 170 at_timestamp_ns, // 171 view_type, // 172 view_count, // 173 out_head_relation, // 174 out_fovs, // 175 out_poses); // 176 } else { 177 IPC_ERROR(ich->ipc_c, "Cannot handle %u view_count, %u or less supported.", view_count, 178 (uint32_t)IPC_MAX_RAW_VIEWS); 179 assert(false && !"Too large view_count!"); 180 } 181 182 return xret; 183} 184 185static xrt_result_t 186ipc_client_hmd_compute_distortion( 187 struct xrt_device *xdev, uint32_t view, float u, float v, struct xrt_uv_triplet *out_result) 188{ 189 ipc_client_hmd_t *ich = ipc_client_hmd(xdev); 190 xrt_result_t xret; 191 192 xret = ipc_call_device_compute_distortion( // 193 ich->ipc_c, // 194 ich->device_id, // 195 view, // 196 u, // 197 v, // 198 out_result); // 199 200 IPC_CHK_ALWAYS_RET(ich->ipc_c, xret, "ipc_call_device_compute_distortion"); 201} 202 203static bool 204ipc_client_hmd_is_form_factor_available(struct xrt_device *xdev, enum xrt_form_factor form_factor) 205{ 206 ipc_client_hmd_t *ich = ipc_client_hmd(xdev); 207 xrt_result_t xret; 208 209 bool available = false; 210 xret = ipc_call_device_is_form_factor_available( // 211 ich->ipc_c, // 212 ich->device_id, // 213 form_factor, // 214 &available); // 215 IPC_CHK_ONLY_PRINT(ich->ipc_c, xret, "ipc_call_device_is_form_factor_available"); 216 217 return available; 218} 219 220static xrt_result_t 221ipc_client_hmd_get_visibility_mask(struct xrt_device *xdev, 222 enum xrt_visibility_mask_type type, 223 uint32_t view_index, 224 struct xrt_visibility_mask **out_mask) 225{ 226 ipc_client_hmd_t *ich = ipc_client_hmd(xdev); 227 struct ipc_connection *ipc_c = ich->ipc_c; 228 struct xrt_visibility_mask *mask = NULL; 229 xrt_result_t xret; 230 231 ipc_client_connection_lock(ipc_c); 232 233 xret = ipc_send_device_get_visibility_mask_locked(ipc_c, ich->device_id, type, view_index); 234 IPC_CHK_WITH_GOTO(ipc_c, xret, "ipc_send_device_get_visibility_mask_locked", err_mask_unlock); 235 236 uint32_t mask_size; 237 xret = ipc_receive_device_get_visibility_mask_locked(ipc_c, &mask_size); 238 IPC_CHK_WITH_GOTO(ipc_c, xret, "ipc_receive_device_get_visibility_mask_locked", err_mask_unlock); 239 240 mask = U_CALLOC_WITH_CAST(struct xrt_visibility_mask, mask_size); 241 if (mask == NULL) { 242 IPC_ERROR(ich->ipc_c, "failed to allocate xrt_visibility_mask"); 243 goto err_mask_unlock; 244 } 245 246 xret = ipc_receive(&ipc_c->imc, mask, mask_size); 247 IPC_CHK_WITH_GOTO(ipc_c, xret, "ipc_receive", err_mask_free); 248 249 *out_mask = mask; 250 ipc_client_connection_unlock(ipc_c); 251 252 return XRT_SUCCESS; 253 254err_mask_free: 255 free(mask); 256err_mask_unlock: 257 ipc_client_connection_unlock(ipc_c); 258 return XRT_ERROR_IPC_FAILURE; 259} 260 261static void 262ipc_client_hmd_destroy(struct xrt_device *xdev) 263{ 264 ipc_client_hmd_t *ich = ipc_client_hmd(xdev); 265 266 // Remove the variable tracking. 267 u_var_remove_root(ich); 268 269 // Free and de-init the shared things. 270 ipc_client_xdev_fini(ich); 271 272 // Free this device with the helper. 273 u_device_free(&ich->base); 274} 275 276static xrt_result_t 277ipc_client_hmd_get_brightness(struct xrt_device *xdev, float *out_brightness) 278{ 279 ipc_client_hmd_t *ich = ipc_client_hmd(xdev); 280 struct ipc_connection *ipc_c = ich->ipc_c; 281 xrt_result_t xret; 282 283 ipc_client_connection_lock(ipc_c); 284 285 xret = ipc_call_device_get_brightness(ipc_c, ich->device_id, out_brightness); 286 IPC_CHK_ONLY_PRINT(ipc_c, xret, "ipc_call_device_get_brightness"); 287 288 ipc_client_connection_unlock(ipc_c); 289 290 return xret; 291} 292 293static xrt_result_t 294ipc_client_hmd_set_brightness(struct xrt_device *xdev, float brightness, bool relative) 295{ 296 ipc_client_hmd_t *ich = ipc_client_hmd(xdev); 297 struct ipc_connection *ipc_c = ich->ipc_c; 298 xrt_result_t xret; 299 300 ipc_client_connection_lock(ipc_c); 301 302 xret = ipc_call_device_set_brightness(ipc_c, ich->device_id, brightness, relative); 303 IPC_CHK_ONLY_PRINT(ipc_c, xret, "ipc_call_device_set_brightness"); 304 305 ipc_client_connection_unlock(ipc_c); 306 307 return xret; 308} 309 310/*! 311 * @public @memberof ipc_client_hmd 312 */ 313struct xrt_device * 314ipc_client_hmd_create(struct ipc_connection *ipc_c, struct xrt_tracking_origin *xtrack, uint32_t device_id) 315{ 316 // Convenience helper. 317 struct ipc_shared_memory *ism = ipc_c->ism; 318 319 // Allocate a HMD device. 320 enum u_device_alloc_flags flags = (enum u_device_alloc_flags)(U_DEVICE_ALLOC_HMD); 321 ipc_client_hmd_t *ich = U_DEVICE_ALLOCATE(ipc_client_hmd_t, flags, 0, 0); 322 323 // Fills in almost everything a regular device needs. 324 ipc_client_xdev_init(ich, ipc_c, xtrack, device_id, ipc_client_hmd_destroy); 325 326 // Fill in needed HMD functions, and destroy. 327 ich->base.get_view_poses = ipc_client_hmd_get_view_poses; 328 ich->base.compute_distortion = ipc_client_hmd_compute_distortion; 329 ich->base.is_form_factor_available = ipc_client_hmd_is_form_factor_available; 330 ich->base.get_visibility_mask = ipc_client_hmd_get_visibility_mask; 331 ich->base.get_brightness = ipc_client_hmd_get_brightness; 332 ich->base.set_brightness = ipc_client_hmd_set_brightness; 333 334 // Setup blend-modes. 335 ich->base.hmd->blend_mode_count = ipc_c->ism->hmd.blend_mode_count; 336 for (int i = 0; i < XRT_MAX_DEVICE_BLEND_MODES; i++) { 337 ich->base.hmd->blend_modes[i] = ipc_c->ism->hmd.blend_modes[i]; 338 } 339 340 // Setup the views. 341 ich->base.hmd->view_count = ism->hmd.view_count; 342 for (uint32_t i = 0; i < ich->base.hmd->view_count; ++i) { 343 ich->base.hmd->views[i].display.w_pixels = ipc_c->ism->hmd.views[i].display.w_pixels; 344 ich->base.hmd->views[i].display.h_pixels = ipc_c->ism->hmd.views[i].display.h_pixels; 345 } 346 347 // Distortion information, fills in xdev->compute_distortion(). 348 u_distortion_mesh_set_none(&ich->base); 349 350 // Setup variable tracker. 351 u_var_add_root(ich, ich->base.str, true); 352 u_var_add_ro_u32(ich, &ich->device_id, "device_id"); 353 354 return &ich->base; 355}