The open source OpenXR runtime
at prediction 270 lines 8.7 kB view raw
1// Copyright 2020-2024, Collabora, Ltd. 2// SPDX-License-Identifier: BSL-1.0 3/*! 4 * @file 5 * @brief Sample HMD device, use as a starting point to make your own device driver. 6 * 7 * 8 * Based largely on simulated_hmd.c 9 * 10 * @author Jakob Bornecrantz <jakob@collabora.com> 11 * @author Rylie Pavlik <rylie.pavlik@collabora.com> 12 * @ingroup drv_sample 13 */ 14 15#include "os/os_time.h" 16#include "xrt/xrt_defines.h" 17#include "xrt/xrt_device.h" 18 19#include "math/m_relation_history.h" 20#include "math/m_api.h" 21#include "math/m_mathinclude.h" // IWYU pragma: keep 22 23#include "util/u_debug.h" 24#include "util/u_device.h" 25#include "util/u_distortion_mesh.h" 26#include "util/u_logging.h" 27#include "util/u_misc.h" 28#include "util/u_time.h" 29#include "util/u_var.h" 30#include "util/u_visibility_mask.h" 31#include "xrt/xrt_results.h" 32 33#include <stdio.h> 34 35 36/* 37 * 38 * Structs and defines. 39 * 40 */ 41 42/*! 43 * A sample HMD device. 44 * 45 * @implements xrt_device 46 */ 47struct sample_hmd 48{ 49 struct xrt_device base; 50 51 struct xrt_pose pose; 52 53 enum u_logging_level log_level; 54 55 // has built-in mutex so thread safe 56 struct m_relation_history *relation_hist; 57}; 58 59 60/// Casting helper function 61static inline struct sample_hmd * 62sample_hmd(struct xrt_device *xdev) 63{ 64 return (struct sample_hmd *)xdev; 65} 66 67DEBUG_GET_ONCE_LOG_OPTION(sample_log, "SAMPLE_LOG", U_LOGGING_WARN) 68 69#define HMD_TRACE(hmd, ...) U_LOG_XDEV_IFL_T(&hmd->base, hmd->log_level, __VA_ARGS__) 70#define HMD_DEBUG(hmd, ...) U_LOG_XDEV_IFL_D(&hmd->base, hmd->log_level, __VA_ARGS__) 71#define HMD_INFO(hmd, ...) U_LOG_XDEV_IFL_I(&hmd->base, hmd->log_level, __VA_ARGS__) 72#define HMD_ERROR(hmd, ...) U_LOG_XDEV_IFL_E(&hmd->base, hmd->log_level, __VA_ARGS__) 73 74static void 75sample_hmd_destroy(struct xrt_device *xdev) 76{ 77 struct sample_hmd *hmd = sample_hmd(xdev); 78 79 // Remove the variable tracking. 80 u_var_remove_root(hmd); 81 82 83 m_relation_history_destroy(&hmd->relation_hist); 84 85 u_device_free(&hmd->base); 86} 87 88static xrt_result_t 89sample_hmd_update_inputs(struct xrt_device *xdev) 90{ 91 /* 92 * Empty for the sample driver, if you need to you should 93 * put code to update the attached inputs fields. If not you can use 94 * the u_device_noop_update_inputs helper to make it a no-op. 95 */ 96 return XRT_SUCCESS; 97} 98 99static xrt_result_t 100sample_hmd_get_tracked_pose(struct xrt_device *xdev, 101 enum xrt_input_name name, 102 int64_t at_timestamp_ns, 103 struct xrt_space_relation *out_relation) 104{ 105 struct sample_hmd *hmd = sample_hmd(xdev); 106 107 if (name != XRT_INPUT_GENERIC_HEAD_POSE) { 108 U_LOG_XDEV_UNSUPPORTED_INPUT(&hmd->base, hmd->log_level, name); 109 return XRT_ERROR_INPUT_UNSUPPORTED; 110 } 111 112 struct xrt_space_relation relation = XRT_SPACE_RELATION_ZERO; 113 114 enum m_relation_history_result history_result = 115 m_relation_history_get(hmd->relation_hist, at_timestamp_ns, &relation); 116 if (history_result == M_RELATION_HISTORY_RESULT_INVALID) { 117 // If you get in here, it means you did not push any poses into the relation history. 118 // You may want to handle this differently. 119 HMD_ERROR(hmd, "Internal error: no poses pushed?"); 120 } 121 122 if ((relation.relation_flags & XRT_SPACE_RELATION_ORIENTATION_VALID_BIT) != 0) { 123 // If we provide an orientation, make sure that it is normalized. 124 math_quat_normalize(&relation.pose.orientation); 125 } 126 127 *out_relation = relation; 128 return XRT_SUCCESS; 129} 130 131static xrt_result_t 132sample_hmd_get_view_poses(struct xrt_device *xdev, 133 const struct xrt_vec3 *default_eye_relation, 134 int64_t at_timestamp_ns, 135 enum xrt_view_type view_type, 136 uint32_t view_count, 137 struct xrt_space_relation *out_head_relation, 138 struct xrt_fov *out_fovs, 139 struct xrt_pose *out_poses) 140{ 141 /* 142 * For HMDs you can call this function or directly set 143 * the `get_view_poses` function on the device to it. 144 */ 145 return u_device_get_view_poses( // 146 xdev, // 147 default_eye_relation, // 148 at_timestamp_ns, // 149 view_type, // 150 view_count, // 151 out_head_relation, // 152 out_fovs, // 153 out_poses); // 154} 155 156static xrt_result_t 157sample_hmd_get_visibility_mask(struct xrt_device *xdev, 158 enum xrt_visibility_mask_type type, 159 uint32_t view_index, 160 struct xrt_visibility_mask **out_mask) 161{ 162 struct xrt_fov fov = xdev->hmd->distortion.fov[view_index]; 163 u_visibility_mask_get_default(type, &fov, out_mask); 164 return XRT_SUCCESS; 165} 166 167struct xrt_device * 168sample_hmd_create(void) 169{ 170 // This indicates you won't be using Monado's built-in tracking algorithms. 171 enum u_device_alloc_flags flags = 172 (enum u_device_alloc_flags)(U_DEVICE_ALLOC_HMD | U_DEVICE_ALLOC_TRACKING_NONE); 173 174 struct sample_hmd *hmd = U_DEVICE_ALLOCATE(struct sample_hmd, flags, 1, 0); 175 176 // This list should be ordered, most preferred first. 177 size_t idx = 0; 178 hmd->base.hmd->blend_modes[idx++] = XRT_BLEND_MODE_OPAQUE; 179 hmd->base.hmd->blend_mode_count = idx; 180 181 hmd->base.update_inputs = sample_hmd_update_inputs; 182 hmd->base.get_tracked_pose = sample_hmd_get_tracked_pose; 183 hmd->base.get_view_poses = sample_hmd_get_view_poses; 184 hmd->base.get_visibility_mask = sample_hmd_get_visibility_mask; 185 hmd->base.destroy = sample_hmd_destroy; 186 187 // Distortion information, fills in xdev->compute_distortion(). 188 u_distortion_mesh_set_none(&hmd->base); 189 190 // populate this with something more complex if required 191 // hmd->base.compute_distortion = sample_hmd_compute_distortion; 192 193 hmd->pose = (struct xrt_pose)XRT_POSE_IDENTITY; 194 hmd->log_level = debug_get_log_option_sample_log(); 195 196 // Print name. 197 snprintf(hmd->base.str, XRT_DEVICE_NAME_LEN, "Sample HMD"); 198 snprintf(hmd->base.serial, XRT_DEVICE_NAME_LEN, "Sample HMD S/N"); 199 200 m_relation_history_create(&hmd->relation_hist); 201 202 // Setup input. 203 hmd->base.name = XRT_DEVICE_GENERIC_HMD; 204 hmd->base.device_type = XRT_DEVICE_TYPE_HMD; 205 hmd->base.inputs[0].name = XRT_INPUT_GENERIC_HEAD_POSE; 206 hmd->base.supported.orientation_tracking = true; 207 hmd->base.supported.position_tracking = true; 208 209 // Set up display details 210 // refresh rate 211 hmd->base.hmd->screens[0].nominal_frame_interval_ns = time_s_to_ns(1.0f / 90.0f); 212 hmd->base.hmd->screens[0].scanout_direction = XRT_SCANOUT_DIRECTION_NONE; 213 hmd->base.hmd->screens[0].scanout_time_ns = 0; 214 215 const double hFOV = 90 * (M_PI / 180.0); 216 const double vFOV = 96.73 * (M_PI / 180.0); 217 // center of projection 218 const double hCOP = 0.529; 219 const double vCOP = 0.5; 220 if ( 221 /* right eye */ 222 !math_compute_fovs(1, hCOP, hFOV, 1, vCOP, vFOV, &hmd->base.hmd->distortion.fov[1]) || 223 /* 224 * left eye - same as right eye, except the horizontal center of projection is moved in the opposite 225 * direction now 226 */ 227 !math_compute_fovs(1, 1.0 - hCOP, hFOV, 1, vCOP, vFOV, &hmd->base.hmd->distortion.fov[0])) { 228 // If those failed, it means our math was impossible. 229 HMD_ERROR(hmd, "Failed to setup basic device info"); 230 sample_hmd_destroy(&hmd->base); 231 return NULL; 232 } 233 const int panel_w = 1080; 234 const int panel_h = 1200; 235 236 // Single "screen" (always the case) 237 hmd->base.hmd->screens[0].w_pixels = panel_w * 2; 238 hmd->base.hmd->screens[0].h_pixels = panel_h; 239 240 // Left, Right 241 for (uint8_t eye = 0; eye < 2; ++eye) { 242 hmd->base.hmd->views[eye].display.w_pixels = panel_w; 243 hmd->base.hmd->views[eye].display.h_pixels = panel_h; 244 hmd->base.hmd->views[eye].viewport.y_pixels = 0; 245 hmd->base.hmd->views[eye].viewport.w_pixels = panel_w; 246 hmd->base.hmd->views[eye].viewport.h_pixels = panel_h; 247 // if rotation is not identity, the dimensions can get more complex. 248 hmd->base.hmd->views[eye].rot = u_device_rotation_ident; 249 } 250 // left eye starts at x=0, right eye starts at x=panel_width 251 hmd->base.hmd->views[0].viewport.x_pixels = 0; 252 hmd->base.hmd->views[1].viewport.x_pixels = panel_w; 253 254 // Distortion information, fills in xdev->compute_distortion(). 255 u_distortion_mesh_set_none(&hmd->base); 256 257 // Just put an initial identity value in the tracker 258 struct xrt_space_relation identity = XRT_SPACE_RELATION_ZERO; 259 identity.relation_flags = (enum xrt_space_relation_flags)(XRT_SPACE_RELATION_ORIENTATION_TRACKED_BIT | 260 XRT_SPACE_RELATION_ORIENTATION_VALID_BIT); 261 uint64_t now = os_monotonic_get_ns(); 262 m_relation_history_push(hmd->relation_hist, &identity, now); 263 264 // Setup variable tracker: Optional but useful for debugging 265 u_var_add_root(hmd, "Sample HMD", true); 266 u_var_add_log_level(hmd, &hmd->log_level, "log_level"); 267 268 269 return &hmd->base; 270}