File oxr_relay.hpp
File List > openxr_interface > oxr_relay.hpp
Go to the documentation of this file
#pragma once
#ifndef USING_OPENXR
# define USING_OPENXR
#endif
#define DOUBLE_INCLUDE
#include "illixr/data_format/latency_data.hpp"
#ifdef ILLIXR_ENABLE_BOBA
# include "illixr/data_format/openxr_view_frame.hpp"
#endif
#include "illixr/data_format/poses/combined_pose.hpp"
#ifdef ILLIXR_ENABLE_QUEST_CONTROLLERS
# include "illixr/data_format/quest_controller.hpp"
#endif
#ifdef ILLIXR_ENABLE_BOBA
# include "illixr/data_format/serialization/openxr_view_frame.hpp"
# include "illixr/data_format/serialization/quest_controller.hpp"
#endif
#include "illixr/switchboard.hpp"
#include "illixr/threadloop.hpp"
#undef DOUBLE_INCLUDE
#include
#include
#include
#include
#include
#define OXR_CheckErrors(cmd, pfunc) \
do { \
XrResult res = cmd; \
if (XR_FAILED(res)) { \
spdlog::get("illixr")->error("OpenXR error {} at {}:{} from {}", static_cast(res), __FILE__, __LINE__, \
#pfunc); \
throw std::runtime_error("Call failed"); \
} \
} while (0)
#define OXR(func) OXR_CheckErrors(func, #func);
namespace ILLIXR {
class oxr_interface;
struct pose_history_entry {
data_format::pose::xrt_space_relation pose;
time_point generated_time;
XrTime xr_time;
};
class oxr_relay : public threadloop {
public:
oxr_relay(const std::string& name, phonebook* pb);
~oxr_relay() override;
bool get_pose_history(uint64_t id, pose_history_entry& out_entry) const;
#ifdef ILLIXR_ENABLE_BOBA
void publish_boba_input(XrTime predicted_time, XrDuration predicted_period, XrBool32 should_render,
XrViewStateFlags view_flags, const XrView views[2], const XrViewConfigurationView view_configs[2]);
#endif
protected:
skip_option _p_should_skip() override;
void _p_one_iteration() override;
private:
friend oxr_interface;
void initialize(XrInstance instance, XrSession session, XrSpace local, XrSpace view);
void destroy();
void update_time(XrTime time) {
predicted_time_.store(time);
}
// Pose tracking
XrResult get_head_pose(XrTime time, data_format::pose::head_pose_type* out_pose);
// ==================== Time offset calibration ====================
void calibrate_time_offsets();
// ==================== Hand Tracking ====================
bool init_hand_tracking();
void destroy_hand_tracking();
void update_hand_tracking(XrTime predicted_time);
// ==================== Hand Interaction ====================
bool init_hand_interaction();
void destroy_hand_interaction();
void update_hand_interaction(XrTime predicted_time);
#ifdef ILLIXR_ENABLE_QUEST_CONTROLLERS
bool create_controller_actions();
bool suggest_controller_bindings();
void refresh_controller_profiles();
bool query_controller_hand(std::size_t hand_index, XrTime sample_time, data_format::quest_hand_controller* hand);
bool query_controller_pose(XrAction action, XrSpace space, XrPath hand_path, XrTime sample_time,
data_format::quest_controller_pose* pose);
bool query_controller_boolean(XrAction action, XrPath hand_path, data_format::quest_controller_button* button);
bool query_controller_float(XrAction action, XrPath hand_path, float threshold,
data_format::quest_controller_button* button);
bool query_controller_axis(XrAction action, XrPath hand_path, data_format::quest_controller_axis2d* axis);
static void merge_controller_button(data_format::quest_controller_button* destination,
const data_format::quest_controller_button& source);
#endif
bool sync_actions();
void push_poses(XrTime predicted_time);
// ================== Member Variables ====================
const std::shared_ptr<switchboard> switchboard_;
const std::shared_ptr<relative_clock> clock_;
switchboard::network_writer<data_format::pose::combined_pose> combined_pose_writer_;
#ifdef ILLIXR_ENABLE_BOBA
switchboard::network_writer<data_format::quest_controller_input> quest_controller_writer_;
switchboard::network_writer<data_format::openxr_view_frame> openxr_view_writer_;
#endif
switchboard::reader<data_format::network_latency_result> latency_reader_;
data_format::pose::head_pose_type current_head_pose_;
data_format::pose::hand_joint_poses_pair current_hand_poses_;
data_format::pose::hand_interaction_poses_pair current_hand_interactions_;
data_format::pose::palm_poses_pair current_palm_poses_;
XrInstance instance_ = XR_NULL_HANDLE;
XrSession session_ = XR_NULL_HANDLE;
XrSpace local_space_ = XR_NULL_HANDLE;
XrSpace view_space_ = XR_NULL_HANDLE;
std::atomic<XrTime> predicted_time_ = 0;
XrHandTrackerEXT left_hand_tracker_{XR_NULL_HANDLE};
XrHandTrackerEXT right_hand_tracker_{XR_NULL_HANDLE};
PFN_xrCreateHandTrackerEXT xr_create_hand_tracker_{nullptr};
PFN_xrDestroyHandTrackerEXT xr_destroy_hand_tracker_{nullptr};
PFN_xrLocateHandJointsEXT xr_locate_hand_joints_{nullptr};
// ==================== Hand Interaction State ====================
bool hand_interaction_supported_{false};
// ==================== Hand Tracking State ====================
bool hand_tracking_supported_{false};
XrActionSet hand_interaction_action_set_{XR_NULL_HANDLE};
XrPath hand_subaction_paths_[2]{XR_NULL_PATH, XR_NULL_PATH};
XrAction interaction_pose_actions_[data_format::pose::NUM_INTERACTION_POSES]{};
XrSpace interaction_pose_spaces_[2][data_format::pose::NUM_INTERACTION_POSES]{};
XrAction interaction_value_actions_[3]{};
XrAction interaction_ready_actions_[3]{};
XrAction palm_pose_action_{XR_NULL_HANDLE};
XrSpace palm_pose_spaces_[2]{XR_NULL_HANDLE, XR_NULL_HANDLE};
#ifdef ILLIXR_ENABLE_QUEST_CONTROLLERS
// Quest Touch inputs share hand_interaction_action_set_ so the session has
// exactly one xrAttachSessionActionSets call.
XrAction controller_trigger_click_action_{XR_NULL_HANDLE};
XrAction controller_trigger_value_action_{XR_NULL_HANDLE};
XrAction controller_squeeze_value_action_{XR_NULL_HANDLE};
XrAction controller_primary_click_action_{XR_NULL_HANDLE};
XrAction controller_secondary_click_action_{XR_NULL_HANDLE};
XrAction controller_thumbstick_click_action_{XR_NULL_HANDLE};
XrAction controller_thumbstick_axis_action_{XR_NULL_HANDLE};
std::array<data_format::quest_controller_profile, 2> controller_profiles_{data_format::quest_controller_profile::none,
data_format::quest_controller_profile::none};
bool controller_actions_initialized_{false};
#endif
std::mutex actions_mutex_;
#ifdef ILLIXR_ENABLE_BOBA
std::atomic<std::uint64_t> boba_input_sequence_{0};
#endif
bool initialized_{false};
bool xr_time_verified_{false};
bool time_offsets_calibrated_{false};
std::chrono::steady_clock::time_point last_offset_calibration_{};
XrTime monotonic_to_system_offset_ns_{0};
XrTime xr_to_monotonic_offset_ns_{0};
uint64_t counter_{0};
data_format::pose::head_pose_type last_pose_{};
// Map from combined_pose id to the pose that was sent with that id.
std::map<uint64_t, pose_history_entry> pose_history_;
mutable std::mutex pose_history_mutex_;
static constexpr size_t MAX_POSE_HISTORY = 240;
};
} // namespace ILLIXR