The open source OpenXR runtime
at main 723 lines 24 kB view raw
1// Copyright 2020-2025, Collabora, Ltd. 2// Copyright 2025, Beyley Cardellio 3// SPDX-License-Identifier: BSL-1.0 4/*! 5 * @file 6 * @brief Driver for the Oculus Rift. 7 * 8 * Based largely on simulated_hmd.c, with reference to the DK1/DK2 firmware and OpenHMD's rift driver. 9 * 10 * @author Jakob Bornecrantz <jakob@collabora.com> 11 * @author Rylie Pavlik <rylie.pavlik@collabora.com> 12 * @author Beyley Cardellio <ep1cm1n10n123@gmail.com> 13 * @ingroup drv_rift 14 */ 15 16#include "os/os_time.h" 17#include "xrt/xrt_defines.h" 18#include "xrt/xrt_device.h" 19 20#include "rift_interface.h" 21#include "rift_distortion.h" 22 23#include "math/m_relation_history.h" 24#include "math/m_clock_tracking.h" 25#include "math/m_api.h" 26#include "math/m_vec2.h" 27#include "math/m_mathinclude.h" // IWYU pragma: keep 28 29#include "util/u_debug.h" 30#include "util/u_device.h" 31#include "util/u_distortion_mesh.h" 32#include "util/u_logging.h" 33#include "util/u_misc.h" 34#include "util/u_time.h" 35#include "util/u_var.h" 36#include "util/u_visibility_mask.h" 37#include "util/u_trace_marker.h" 38#include "xrt/xrt_results.h" 39 40#include <stdio.h> 41#include <assert.h> 42 43/* 44 * 45 * Structs and defines. 46 * 47 */ 48 49DEBUG_GET_ONCE_LOG_OPTION(rift_log, "RIFT_LOG", U_LOGGING_WARN) 50 51#define HMD_TRACE(hmd, ...) U_LOG_XDEV_IFL_T(&hmd->base, hmd->log_level, __VA_ARGS__) 52#define HMD_DEBUG(hmd, ...) U_LOG_XDEV_IFL_D(&hmd->base, hmd->log_level, __VA_ARGS__) 53#define HMD_INFO(hmd, ...) U_LOG_XDEV_IFL_I(&hmd->base, hmd->log_level, __VA_ARGS__) 54#define HMD_WARN(hmd, ...) U_LOG_XDEV_IFL_W(&hmd->base, hmd->log_level, __VA_ARGS__) 55#define HMD_ERROR(hmd, ...) U_LOG_XDEV_IFL_E(&hmd->base, hmd->log_level, __VA_ARGS__) 56 57/* 58 * 59 * Headset functions 60 * 61 */ 62 63static int 64rift_send_report(struct rift_hmd *hmd, uint8_t report_id, void *data, size_t data_length) 65{ 66 int result; 67 68 if (data_length > REPORT_MAX_SIZE - 1) { 69 return -1; 70 } 71 72 uint8_t buffer[REPORT_MAX_SIZE]; 73 buffer[0] = report_id; 74 memcpy(buffer + 1, data, data_length); 75 76 result = os_hid_set_feature(hmd->hid_dev, buffer, data_length + 1); 77 if (result < 0) { 78 return result; 79 } 80 81 return 0; 82} 83 84static int 85rift_get_report(struct rift_hmd *hmd, uint8_t report_id, uint8_t *out, size_t out_len) 86{ 87 return os_hid_get_feature(hmd->hid_dev, report_id, out, out_len); 88} 89 90static int 91rift_send_keepalive(struct rift_hmd *hmd) 92{ 93 struct dk2_report_keepalive_mux report = {0, IN_REPORT_DK2, 94 KEEPALIVE_INTERVAL_NS / 1000000}; // convert ns to ms 95 96 int result = rift_send_report(hmd, FEATURE_REPORT_KEEPALIVE_MUX, &report, sizeof(report)); 97 98 if (result < 0) { 99 return result; 100 } 101 102 hmd->last_keepalive_time = os_monotonic_get_ns(); 103 HMD_TRACE(hmd, "Sent keepalive at time %ld", hmd->last_keepalive_time); 104 105 return 0; 106} 107 108static int 109rift_get_config(struct rift_hmd *hmd, struct rift_config_report *config) 110{ 111 uint8_t buf[REPORT_MAX_SIZE] = {0}; 112 113 int result = rift_get_report(hmd, FEATURE_REPORT_CONFIG, buf, sizeof(buf)); 114 if (result < 0) { 115 return result; 116 } 117 118 // FIXME: handle endian differences 119 memcpy(config, buf + 1, sizeof(*config)); 120 121 // this value is hardcoded in the DK1 and DK2 firmware 122 if ((hmd->variant == RIFT_VARIANT_DK1 || hmd->variant == RIFT_VARIANT_DK2) && 123 config->sample_rate != IMU_SAMPLE_RATE) { 124 HMD_ERROR(hmd, "Got invalid config from headset, got sample rate %d when expected %d", 125 config->sample_rate, IMU_SAMPLE_RATE); 126 return -1; 127 } 128 129 return 0; 130} 131 132static int 133rift_get_display_info(struct rift_hmd *hmd, struct rift_display_info_report *display_info) 134{ 135 uint8_t buf[REPORT_MAX_SIZE] = {0}; 136 137 int result = rift_get_report(hmd, FEATURE_REPORT_DISPLAY_INFO, buf, sizeof(buf)); 138 if (result < 0) { 139 return result; 140 } 141 142 // FIXME: handle endian differences 143 memcpy(display_info, buf + 1, sizeof(*display_info)); 144 145 return 0; 146} 147 148static int 149rift_get_lens_distortion(struct rift_hmd *hmd, struct rift_lens_distortion_report *lens_distortion) 150{ 151 uint8_t buf[REPORT_MAX_SIZE] = {0}; 152 153 int result = rift_get_report(hmd, FEATURE_REPORT_LENS_DISTORTION, buf, sizeof(buf)); 154 if (result < 0) { 155 return result; 156 } 157 158 memcpy(lens_distortion, buf + 1, sizeof(*lens_distortion)); 159 160 return 0; 161} 162 163static int 164rift_set_config(struct rift_hmd *hmd, struct rift_config_report *config) 165{ 166 return rift_send_report(hmd, FEATURE_REPORT_CONFIG, config, sizeof(*config)); 167} 168 169/* 170 * 171 * Driver functions 172 * 173 */ 174 175static void 176rift_hmd_destroy(struct xrt_device *xdev) 177{ 178 struct rift_hmd *hmd = rift_hmd(xdev); 179 180 // Remove the variable tracking. 181 u_var_remove_root(hmd); 182 183 if (hmd->sensor_thread.initialized) 184 os_thread_helper_stop_and_wait(&hmd->sensor_thread); 185 186 if (hmd->clock_tracker) 187 m_clock_windowed_skew_tracker_destroy(hmd->clock_tracker); 188 189 m_relation_history_destroy(&hmd->relation_hist); 190 191 if (hmd->lens_distortions) 192 free(hmd->lens_distortions); 193 194 u_device_free(&hmd->base); 195} 196 197static xrt_result_t 198rift_hmd_get_tracked_pose(struct xrt_device *xdev, 199 enum xrt_input_name name, 200 int64_t at_timestamp_ns, 201 struct xrt_space_relation *out_relation) 202{ 203 struct rift_hmd *hmd = rift_hmd(xdev); 204 205 if (name != XRT_INPUT_GENERIC_HEAD_POSE) { 206 U_LOG_XDEV_UNSUPPORTED_INPUT(&hmd->base, hmd->log_level, name); 207 return XRT_ERROR_INPUT_UNSUPPORTED; 208 } 209 210 struct xrt_space_relation relation = XRT_SPACE_RELATION_ZERO; 211 212 enum m_relation_history_result history_result = 213 m_relation_history_get(hmd->relation_hist, at_timestamp_ns, &relation); 214 if (history_result == M_RELATION_HISTORY_RESULT_INVALID) { 215 // If you get in here, it means you did not push any poses into the relation history. 216 // You may want to handle this differently. 217 HMD_ERROR(hmd, "Internal error: no poses pushed?"); 218 } 219 220 if ((relation.relation_flags & XRT_SPACE_RELATION_ORIENTATION_VALID_BIT) != 0) { 221 // If we provide an orientation, make sure that it is normalized. 222 math_quat_normalize(&relation.pose.orientation); 223 } 224 225 *out_relation = relation; 226 return XRT_SUCCESS; 227} 228 229static xrt_result_t 230rift_hmd_get_view_poses(struct xrt_device *xdev, 231 const struct xrt_vec3 *default_eye_relation, 232 int64_t at_timestamp_ns, 233 enum xrt_view_type view_type, 234 uint32_t view_count, 235 struct xrt_space_relation *out_head_relation, 236 struct xrt_fov *out_fovs, 237 struct xrt_pose *out_poses) 238{ 239 struct rift_hmd *hmd = rift_hmd(xdev); 240 241 return u_device_get_view_poses( // 242 xdev, // 243 &(struct xrt_vec3){hmd->extra_display_info.icd, 0.0f, 0.0f}, // 244 at_timestamp_ns, // 245 view_type, // 246 view_count, // 247 out_head_relation, // 248 out_fovs, // 249 out_poses); 250} 251 252static xrt_result_t 253rift_hmd_get_visibility_mask(struct xrt_device *xdev, 254 enum xrt_visibility_mask_type type, 255 uint32_t view_index, 256 struct xrt_visibility_mask **out_mask) 257{ 258 struct xrt_fov fov = xdev->hmd->distortion.fov[view_index]; 259 u_visibility_mask_get_default(type, &fov, out_mask); 260 return XRT_SUCCESS; 261} 262 263static float 264rift_decode_fixed_point_uint16(uint16_t value, uint16_t zero_value, int fractional_bits) 265{ 266 float value_float = (float)value; 267 value_float -= (float)zero_value; 268 value_float *= 1.0f / (float)(1 << fractional_bits); 269 return value_float; 270} 271 272static void 273rift_parse_distortion_report(struct rift_lens_distortion_report *report, struct rift_lens_distortion *out) 274{ 275 out->distortion_version = report->distortion_version; 276 277 switch (report->distortion_version) { 278 case RIFT_LENS_DISTORTION_LCSV_CATMULL_ROM_10_VERSION_1: { 279 struct rift_catmull_rom_distortion_report_data report_data = report->data.lcsv_catmull_rom_10; 280 struct rift_catmull_rom_distortion_data data; 281 282 out->eye_relief = MICROMETERS_TO_METERS(report_data.eye_relief); 283 284 for (uint16_t i = 0; i < CATMULL_COEFFICIENTS; i += 1) { 285 data.k[i] = rift_decode_fixed_point_uint16(report_data.k[i], 0, 14); 286 } 287 data.max_r = rift_decode_fixed_point_uint16(report_data.max_r, 0, 14); 288 data.meters_per_tan_angle_at_center = 289 rift_decode_fixed_point_uint16(report_data.meters_per_tan_angle_at_center, 0, 19); 290 for (uint16_t i = 0; i < CHROMATIC_ABBERATION_COEFFEICENT_COUNT; i += 1) { 291 data.chromatic_abberation[i] = 292 rift_decode_fixed_point_uint16(report_data.chromatic_abberation[i], 0x8000, 19); 293 } 294 295 out->data.lcsv_catmull_rom_10 = data; 296 break; 297 } 298 default: return; 299 } 300} 301 302/* 303 * Decode 3 tightly packed 21 bit values from 4 bytes. 304 * We unpack them in the higher 21 bit values first and then shift 305 * them down to the lower in order to get the sign bits correct. 306 * 307 * Code taken/reformatted from OpenHMD's rift driver 308 */ 309static void 310rift_decode_sample(const uint8_t *in, int32_t *out) 311{ 312 int x = (in[0] << 24) | (in[1] << 16) | ((in[2] & 0xF8) << 8); 313 int y = ((in[2] & 0x07) << 29) | (in[3] << 21) | (in[4] << 13) | ((in[5] & 0xC0) << 5); 314 int z = ((in[5] & 0x3F) << 26) | (in[6] << 18) | (in[7] << 10); 315 316 out[0] = x >> 11; 317 out[1] = y >> 11; 318 out[2] = z >> 11; 319} 320 321static void 322rift_sample_to_imu_space(const int32_t *in, struct xrt_vec3 *out) 323{ 324 out->x = (float)in[0] * 0.0001f; 325 out->y = (float)in[1] * 0.0001f; 326 out->z = (float)in[2] * 0.0001f; 327} 328 329static int 330sensor_thread_tick(struct rift_hmd *hmd) 331{ 332 uint8_t buf[REPORT_MAX_SIZE]; 333 int result; 334 335 int64_t now = os_monotonic_get_ns(); 336 337 if (now - hmd->last_keepalive_time > KEEPALIVE_SEND_RATE_NS) { 338 result = rift_send_keepalive(hmd); 339 340 if (result < 0) { 341 HMD_ERROR(hmd, "Got error sending keepalive, assuming fatal, reason %d", result); 342 return result; 343 } 344 } 345 346 result = os_hid_read(hmd->hid_dev, buf, sizeof(buf), IMU_SAMPLE_RATE); 347 348 if (result < 0) { 349 HMD_ERROR(hmd, "Got error reading from device, assuming fatal, reason %d", result); 350 return result; 351 } 352 353 if (result == 0) { 354 HMD_WARN(hmd, "Timed out waiting for packet from headset, packets should come in at %dhz", 355 IMU_SAMPLE_RATE); 356 return 0; 357 } 358 359 switch (hmd->variant) { 360 case RIFT_VARIANT_DK2: { 361 // skip unknown commands 362 if (buf[0] != IN_REPORT_DK2) { 363 HMD_WARN(hmd, "Skipping unknown IN command %d", buf[0]); 364 return 0; 365 } 366 367 struct dk2_in_report report; 368 369 // don't treat invalid IN reports as fatal, just ignore them 370 if (result < (int)sizeof(report)) { 371 HMD_WARN(hmd, "Got malformed DK2 IN report with size %d", result); 372 return 0; 373 } 374 375 // TODO: handle endianness 376 memcpy(&report, buf + 1, sizeof(report)); 377 378 // if there's no samples, just do nothing. 379 if (report.num_samples == 0) { 380 return 0; 381 } 382 383 if (!hmd->processed_sample_packet) { 384 hmd->last_remote_sample_time_us = report.sample_timestamp; 385 hmd->processed_sample_packet = true; 386 } 387 388 // wrap-around intentional and A-OK, given these are unsigned 389 uint32_t remote_sample_delta_us = report.sample_timestamp - hmd->last_remote_sample_time_us; 390 391 hmd->last_remote_sample_time_us = report.sample_timestamp; 392 393 hmd->last_remote_sample_time_ns += (int64_t)remote_sample_delta_us * OS_NS_PER_USEC; 394 395 m_clock_windowed_skew_tracker_push(hmd->clock_tracker, os_monotonic_get_ns(), 396 hmd->last_remote_sample_time_ns); 397 398 int64_t local_timestamp_ns; 399 // if we haven't synchronized our clocks, just do nothing 400 if (!m_clock_windowed_skew_tracker_to_local(hmd->clock_tracker, hmd->last_remote_sample_time_ns, 401 &local_timestamp_ns)) { 402 return 0; 403 } 404 405 if (report.num_samples > 1) 406 HMD_TRACE(hmd, 407 "Had more than one sample queued! We aren't receiving IN reports fast enough, HMD " 408 "had %d samples in the queue! Having to work back that first sample...", 409 report.num_samples); 410 411 for (int i = 0; i < MIN(DK2_MAX_SAMPLES, report.num_samples); i++) { 412 struct dk2_sample_pack latest_sample_pack = report.samples[i]; 413 414 int32_t accel_raw[3], gyro_raw[3]; 415 rift_decode_sample(latest_sample_pack.accel.data, accel_raw); 416 rift_decode_sample(latest_sample_pack.gyro.data, gyro_raw); 417 418 struct xrt_vec3 accel, gyro; 419 rift_sample_to_imu_space(accel_raw, &accel); 420 rift_sample_to_imu_space(gyro_raw, &gyro); 421 422 // work back the likely timestamp of the current sample 423 // if there's only one sample, then this will always be zero, if there's two or more samples, 424 // the previous samples will be offset by the sample rate of the IMU 425 int64_t sample_local_timestamp_ns = 426 local_timestamp_ns - ((MIN(report.num_samples, DK2_MAX_SAMPLES) - 1) * NS_PER_SAMPLE); 427 428 // update the IMU for that sample 429 m_imu_3dof_update(&hmd->fusion, sample_local_timestamp_ns, &accel, &gyro); 430 431 // push the pose of the IMU for that sample, doing so per sample 432 struct xrt_space_relation relation = XRT_SPACE_RELATION_ZERO; 433 relation.relation_flags = (enum xrt_space_relation_flags)( 434 XRT_SPACE_RELATION_ORIENTATION_TRACKED_BIT | XRT_SPACE_RELATION_ORIENTATION_VALID_BIT); 435 relation.pose.orientation = hmd->fusion.rot; 436 m_relation_history_push(hmd->relation_hist, &relation, sample_local_timestamp_ns); 437 } 438 439 break; 440 } 441 case RIFT_VARIANT_DK1: return 0; 442 } 443 444 return 0; 445} 446 447static void * 448sensor_thread(void *ptr) 449{ 450 U_TRACE_SET_THREAD_NAME("Rift sensor thread"); 451 452 struct rift_hmd *hmd = (struct rift_hmd *)ptr; 453 454 os_thread_helper_lock(&hmd->sensor_thread); 455 456 // uncomment this to be able to see if things are actually progressing as expected in a debugger, without having 457 // to count yourself 458 // #define TICK_DEBUG 459 460 int result = 0; 461#ifdef TICK_DEBUG 462 int ticks = 0; 463#endif 464 465 while (os_thread_helper_is_running_locked(&hmd->sensor_thread) && result >= 0) { 466 os_thread_helper_unlock(&hmd->sensor_thread); 467 468 result = sensor_thread_tick(hmd); 469 470 os_thread_helper_lock(&hmd->sensor_thread); 471#ifdef TICK_DEBUG 472 ticks += 1; 473#endif 474 } 475 476 os_thread_helper_unlock(&hmd->sensor_thread); 477 478 return NULL; 479} 480 481struct rift_hmd * 482rift_hmd_create(struct os_hid_device *dev, enum rift_variant variant, char *device_name, char *serial_number) 483{ 484 int result; 485 486 // This indicates you won't be using Monado's built-in tracking algorithms. 487 enum u_device_alloc_flags flags = 488 (enum u_device_alloc_flags)(U_DEVICE_ALLOC_HMD | U_DEVICE_ALLOC_TRACKING_NONE); 489 490 struct rift_hmd *hmd = U_DEVICE_ALLOCATE(struct rift_hmd, flags, 1, 0); 491 492 hmd->variant = variant; 493 hmd->hid_dev = dev; 494 495 result = rift_send_keepalive(hmd); 496 if (result < 0) { 497 HMD_ERROR(hmd, "Failed to send keepalive to spin up headset, reason %d", result); 498 goto error; 499 } 500 501 result = rift_get_display_info(hmd, &hmd->display_info); 502 if (result < 0) { 503 HMD_ERROR(hmd, "Failed to get device config, reason %d", result); 504 goto error; 505 } 506 HMD_DEBUG(hmd, "Got display info from hmd, res: %dx%d", hmd->display_info.resolution_x, 507 hmd->display_info.resolution_y); 508 509 result = rift_get_config(hmd, &hmd->config); 510 if (result < 0) { 511 HMD_ERROR(hmd, "Failed to get device config, reason %d", result); 512 goto error; 513 } 514 HMD_DEBUG(hmd, "Got config from hmd, config flags: %X", hmd->config.config_flags); 515 516 if (getenv("RIFT_POWER_OVERRIDE") != NULL) { 517 hmd->config.config_flags |= RIFT_CONFIG_REPORT_OVERRIDE_POWER; 518 HMD_INFO(hmd, "Enabling the override power config flag."); 519 } else { 520 hmd->config.config_flags &= ~RIFT_CONFIG_REPORT_OVERRIDE_POWER; 521 HMD_DEBUG(hmd, "Disabling the override power config flag."); 522 } 523 524 // force enable calibration use and auto calibration 525 // this is on by default according to the firmware on DK1 and DK2, 526 // but OpenHMD forces them on, we should do the same, they probably had a reason 527 hmd->config.config_flags |= RIFT_CONFIG_REPORT_USE_CALIBRATION; 528 hmd->config.config_flags |= RIFT_CONFIG_REPORT_AUTO_CALIBRATION; 529 530 hmd->config.interval = 0; 531 532 // update the config 533 result = rift_set_config(hmd, &hmd->config); 534 if (result < 0) { 535 HMD_ERROR(hmd, "Failed to set the device config, reason %d", result); 536 goto error; 537 } 538 539 // read it back 540 result = rift_get_config(hmd, &hmd->config); 541 if (result < 0) { 542 HMD_ERROR(hmd, "Failed to set the device config, reason %d", result); 543 goto error; 544 } 545 HMD_DEBUG(hmd, "After writing, HMD has config flags: %X", hmd->config.config_flags); 546 547 if (getenv("RIFT_USE_FIRMWARE_DISTORTION") != NULL) { 548 // get the lens distortions 549 struct rift_lens_distortion_report lens_distortion; 550 result = rift_get_lens_distortion(hmd, &lens_distortion); 551 if (result < 0) { 552 HMD_ERROR(hmd, "Failed to get lens distortion, reason %d", result); 553 goto error; 554 } 555 556 hmd->num_lens_distortions = lens_distortion.num_distortions; 557 hmd->lens_distortions = calloc(lens_distortion.num_distortions, sizeof(struct rift_lens_distortion)); 558 559 rift_parse_distortion_report(&lens_distortion, &hmd->lens_distortions[lens_distortion.distortion_idx]); 560 // TODO: actually verify we initialize all the distortions. if the headset is working correctly, this 561 // should have happened, but you never know. 562 for (uint16_t i = 1; i < hmd->num_lens_distortions; i++) { 563 result = rift_get_lens_distortion(hmd, &lens_distortion); 564 if (result < 0) { 565 HMD_ERROR(hmd, "Failed to get lens distortion idx %d, reason %d", i, result); 566 goto error; 567 } 568 569 rift_parse_distortion_report(&lens_distortion, 570 &hmd->lens_distortions[lens_distortion.distortion_idx]); 571 } 572 573 // TODO: pick the correct distortion for the eye relief setting the user has picked 574 hmd->distortion_in_use = 0; 575 } else { 576 rift_fill_in_default_distortions(hmd); 577 } 578 579 // fill in extra display info about the headset 580 581 switch (hmd->variant) { 582 case RIFT_VARIANT_DK2: 583 hmd->extra_display_info.screen_gap_meters = 0.0f; 584 hmd->extra_display_info.lens_diameter_meters = 0.04f; 585 break; 586 default: break; 587 } 588 589 // hardcode left eye, probably not ideal, but sure, why not 590 struct rift_distortion_render_info distortion_render_info = rift_get_distortion_render_info(hmd, 0); 591 hmd->extra_display_info.fov = rift_calculate_fov_from_hmd(hmd, &distortion_render_info, 0); 592 hmd->extra_display_info.eye_to_source_ndc = 593 rift_calculate_ndc_scale_and_offset_from_fov(&hmd->extra_display_info.fov); 594 hmd->extra_display_info.eye_to_source_uv = 595 rift_calculate_uv_scale_and_offset_from_ndc_scale_and_offset(hmd->extra_display_info.eye_to_source_ndc); 596 597 size_t idx = 0; 598 hmd->base.hmd->blend_modes[idx++] = XRT_BLEND_MODE_OPAQUE; 599 hmd->base.hmd->blend_mode_count = idx; 600 601 hmd->base.update_inputs = u_device_noop_update_inputs; 602 hmd->base.get_tracked_pose = rift_hmd_get_tracked_pose; 603 hmd->base.get_view_poses = rift_hmd_get_view_poses; 604 hmd->base.get_visibility_mask = rift_hmd_get_visibility_mask; 605 hmd->base.destroy = rift_hmd_destroy; 606 607 hmd->base.hmd->distortion.models = XRT_DISTORTION_MODEL_COMPUTE; 608 hmd->base.hmd->distortion.preferred = XRT_DISTORTION_MODEL_COMPUTE; 609 hmd->base.compute_distortion = rift_hmd_compute_distortion; 610 u_distortion_mesh_fill_in_compute(&hmd->base); 611 612 hmd->pose = (struct xrt_pose)XRT_POSE_IDENTITY; 613 hmd->log_level = debug_get_log_option_rift_log(); 614 615 // Print name. 616 strncpy(hmd->base.str, device_name, XRT_DEVICE_NAME_LEN); 617 strncpy(hmd->base.serial, serial_number, XRT_DEVICE_NAME_LEN); 618 619 m_relation_history_create(&hmd->relation_hist); 620 621 // Setup input. 622 hmd->base.name = XRT_DEVICE_GENERIC_HMD; 623 hmd->base.device_type = XRT_DEVICE_TYPE_HMD; 624 hmd->base.inputs[0].name = XRT_INPUT_GENERIC_HEAD_POSE; 625 hmd->base.supported.orientation_tracking = true; 626 hmd->base.supported.position_tracking = false; // set to true once we are trying to get the sensor 6dof to work 627 628 // Set up display details 629 hmd->base.hmd->screens[0].nominal_frame_interval_ns = time_s_to_ns(1.0f / 75.0f); 630 hmd->base.hmd->screens[0].scanout_direction = XRT_SCANOUT_DIRECTION_NONE; 631 hmd->base.hmd->screens[0].scanout_time_ns = 0; 632 633 hmd->extra_display_info.icd = MICROMETERS_TO_METERS(hmd->display_info.lens_separation); 634 635 char *icd_str = getenv("RIFT_OVERRIDE_ICD"); 636 if (icd_str != NULL) { 637 // mm -> meters 638 float icd = strtof(icd_str, NULL) / 1000.0f; 639 640 // 0 is error, and less than zero is invalid 641 if (icd > 0.0f) { 642 hmd->extra_display_info.icd = icd; 643 HMD_INFO(hmd, "Forcing ICD to %f", hmd->extra_display_info.icd); 644 } else { 645 HMD_ERROR(hmd, "Failed to parse ICD override, expected float in millimeters, got %s", icd_str); 646 } 647 } else { 648 HMD_DEBUG(hmd, "Using default ICD of %f", hmd->extra_display_info.icd); 649 } 650 651 // screen is rotated, so we need to undo that here 652 hmd->base.hmd->screens[0].h_pixels = hmd->display_info.resolution_x; 653 hmd->base.hmd->screens[0].w_pixels = hmd->display_info.resolution_y; 654 655 // TODO: properly apply using rift_extra_display_info.screen_gap_meters, but this isn't necessary on DK2, where 656 // the gap is always 0 657 uint16_t view_width = hmd->display_info.resolution_x / 2; 658 uint16_t view_height = hmd->display_info.resolution_y; 659 660 for (uint32_t i = 0; i < 2; ++i) { 661 hmd->base.hmd->views[i].display.w_pixels = view_width; 662 hmd->base.hmd->views[i].display.h_pixels = view_height; 663 664 hmd->base.hmd->views[i].viewport.x_pixels = 0; 665 hmd->base.hmd->views[i].viewport.y_pixels = (1 - i) * (hmd->display_info.resolution_x / 2); 666 hmd->base.hmd->views[i].viewport.w_pixels = view_height; // screen is rotated, so swap w and h 667 hmd->base.hmd->views[i].viewport.h_pixels = view_width; 668 hmd->base.hmd->views[i].rot = u_device_rotation_left; 669 } 670 671 switch (hmd->variant) { 672 default: 673 case RIFT_VARIANT_DK2: 674 // TODO: figure out how to calculate this programmatically, right now this is hardcoded with data dumped 675 // from oculus' OpenXR runtime, some of the math for this is in rift_distortion.c, used for 676 // calculating distortion 677 hmd->base.hmd->distortion.fov[0].angle_up = 0.92667186; 678 hmd->base.hmd->distortion.fov[0].angle_down = -0.92667186; 679 hmd->base.hmd->distortion.fov[0].angle_left = -0.8138836; 680 hmd->base.hmd->distortion.fov[0].angle_right = 0.82951474; 681 682 hmd->base.hmd->distortion.fov[1].angle_up = 0.92667186; 683 hmd->base.hmd->distortion.fov[1].angle_down = -0.92667186; 684 hmd->base.hmd->distortion.fov[1].angle_left = -0.82951474; 685 hmd->base.hmd->distortion.fov[1].angle_right = 0.8138836; 686 break; 687 } 688 689 // Just put an initial identity value in the tracker 690 struct xrt_space_relation identity = XRT_SPACE_RELATION_ZERO; 691 identity.relation_flags = (enum xrt_space_relation_flags)(XRT_SPACE_RELATION_ORIENTATION_TRACKED_BIT | 692 XRT_SPACE_RELATION_ORIENTATION_VALID_BIT); 693 uint64_t now = os_monotonic_get_ns(); 694 m_relation_history_push(hmd->relation_hist, &identity, now); 695 696 result = os_thread_helper_init(&hmd->sensor_thread); 697 698 if (result < 0) { 699 HMD_ERROR(hmd, "Failed to init os thread helper"); 700 goto error; 701 } 702 703 m_imu_3dof_init(&hmd->fusion, M_IMU_3DOF_USE_GRAVITY_DUR_20MS); 704 hmd->clock_tracker = m_clock_windowed_skew_tracker_alloc(64); 705 706 result = os_thread_helper_start(&hmd->sensor_thread, sensor_thread, hmd); 707 708 if (result < 0) { 709 HMD_ERROR(hmd, "Failed to start sensor thread"); 710 goto error; 711 } 712 713 // Setup variable tracker: Optional but useful for debugging 714 u_var_add_root(hmd, "Rift HMD", true); 715 u_var_add_log_level(hmd, &hmd->log_level, "log_level"); 716 u_var_add_f32(hmd, &hmd->extra_display_info.icd, "ICD"); 717 m_imu_3dof_add_vars(&hmd->fusion, hmd, "3dof_"); 718 719 return hmd; 720error: 721 rift_hmd_destroy(&hmd->base); 722 return NULL; 723}