diff --git a/extension/T5Integration/Glasses.cpp b/extension/T5Integration/Glasses.cpp index 97437ce..0528d5d 100644 --- a/extension/T5Integration/Glasses.cpp +++ b/extension/T5Integration/Glasses.cpp @@ -157,13 +157,24 @@ bool Glasses::allocate_handle(T5_Context context) { LOG_T5_ERROR(result); return false; } - _state.set(GlassesState::CREATED); - _state.clear(GlassesState::UNAVAILABLE); - _scheduler->add_task(monitor_parameters()); return true; } +void Glasses::set_existing(bool exists) { + if (exists == is_existing()) + return; + if (exists) { + if (_state.set_and_was_toggled(GlassesState::EXISTS)) + _scheduler->add_task(monitor_parameters()); + _state.clear(GlassesState::UNAVAILABLE); + } else { + _state.clear(GlassesState::EXISTS); + _state.set(GlassesState::UNAVAILABLE); + disconnect(); + } +} + void Glasses::destroy_handle() { _state.clear_all(); { @@ -174,6 +185,11 @@ void Glasses::destroy_handle() { } CotaskPtr Glasses::monitor_connection() { + //Shouldn't be possible to have multiple of these running at once + MonitorFlags::ScopeGuard guard(_async_functions_running, CONNECTION_MONITOR_FUNC_RUNNING); + if (!guard.was_toggled()) + co_return; + T5_Result result; while (_glasses_handle && _state.is_current(GlassesState::SUSTAIN_CONNECTION)) { @@ -201,28 +217,23 @@ CotaskPtr Glasses::monitor_connection() { std::lock_guard lock(g_t5_exclusivity_group_1); result = t5ReserveGlasses(_glasses_handle, _application_name.c_str()); } - if (result == T5_SUCCESS || result == T5_ERROR_ALREADY_CONNECTED) + if (result == T5_SUCCESS || result == T5_ERROR_ALREADY_CONNECTED) { + _state.clear(GlassesState::UNAVAILABLE); continue; - else if (result == T5_ERROR_UNAVAILABLE) { + } else if (result == T5_ERROR_UNAVAILABLE) { // Some else has the glasses so stop // trying to connect _state.clear(GlassesState::SUSTAIN_CONNECTION); _state.set(GlassesState::UNAVAILABLE); co_return; - } else if (result == T5_ERROR_DEVICE_LOST) { - _state.clear(GlassesState::SUSTAIN_CONNECTION); - co_await run_in_foreground; - LOG_T5_ERROR(result); - destroy_handle(); - co_return; } _state.reset(GlassesState::ERROR); co_await run_in_foreground; LOG_T5_ERROR(result); co_return; } - case kT5_ConnectionState_ExclusiveReservation: - case kT5_ConnectionState_Disconnected: { + case kT5_ConnectionState_Disconnected: + case kT5_ConnectionState_ExclusiveReservation: { _state.clear(GlassesState::READY); { @@ -239,11 +250,6 @@ CotaskPtr Glasses::monitor_connection() { // trying to connect _state.clear(GlassesState::SUSTAIN_CONNECTION); _state.set(GlassesState::UNAVAILABLE); - } else if (result == T5_ERROR_DEVICE_LOST) { - _state.clear(GlassesState::SUSTAIN_CONNECTION); - co_await run_in_foreground; - LOG_T5_ERROR(result); - destroy_handle(); } else { _state.reset(GlassesState::ERROR); co_await run_in_foreground; @@ -278,14 +284,79 @@ CotaskPtr Glasses::monitor_connection() { } } +CotaskPtr Glasses::monitor_unavailable() { + //Shouldn't be possible to have multiple of these running at once + MonitorFlags::ScopeGuard guard(_async_functions_running, UNAVAILABLE_MONITOR_FUNC_RUNNING); + if (!guard.was_toggled()) + co_return; + + T5_Result result; + + while (_glasses_handle && _state.is_current(GlassesState::EXISTS | GlassesState::UNAVAILABLE)) { + T5_ConnectionState connectionState; + + { + std::lock_guard lock(g_t5_exclusivity_group_1); + result = t5GetGlassesConnectionState(_glasses_handle, &connectionState); + } + if (result != T5_SUCCESS) { + // Doesn't seem to be anything recoverable here + co_await run_in_foreground; + LOG_T5_ERROR(result); + _state.reset(GlassesState::ERROR); + co_return; + } + + switch (connectionState) { + case kT5_ConnectionState_NotExclusivelyConnected: { + _state.clear(GlassesState::READY); + { + std::lock_guard lock(g_t5_exclusivity_group_1); + result = t5ReserveGlasses(_glasses_handle, _application_name.c_str()); + } + if (result == T5_SUCCESS) { + _state.clear(GlassesState::UNAVAILABLE); + { + std::lock_guard lock(g_t5_exclusivity_group_1); + result = t5ReleaseGlasses(_glasses_handle); + } + co_return; + } else if (result == T5_ERROR_ALREADY_CONNECTED) { + _state.clear(GlassesState::UNAVAILABLE); + co_return; + } else if (result != T5_ERROR_UNAVAILABLE) { + _state.reset(GlassesState::ERROR); + co_await run_in_foreground; + LOG_T5_ERROR(result); + co_return; + } + break; + } + case kT5_ConnectionState_Disconnected: + case kT5_ConnectionState_ExclusiveReservation: + case kT5_ConnectionState_ExclusiveConnection: { + _state.clear(GlassesState::UNAVAILABLE); + co_return; + } + } + + co_await task_sleep(_poll_rate_for_monitoring); + } +} + CotaskPtr Glasses::monitor_parameters() { + //Shouldn't be possible to have multiple of these running at once + MonitorFlags::ScopeGuard guard(_async_functions_running, PARAMETER_MONITOR_FUNC_RUNNING); + if (!guard.was_toggled()) + co_return; + co_await query_ipd(); co_await query_friendly_name(); T5_Result result; std::vector _changed_params; - while (_glasses_handle && _state.is_current(GlassesState::CREATED)) { + while (_glasses_handle && _state.is_current(GlassesState::EXISTS)) { co_await task_sleep(_poll_rate_for_monitoring); uint16_t buffer_size = 16; @@ -320,6 +391,11 @@ CotaskPtr Glasses::monitor_parameters() { } CotaskPtr Glasses::monitor_wands() { + //Shouldn't be possible to have multiple of these running at once + MonitorFlags::ScopeGuard guard(_async_functions_running, WAND_MONITOR_FUNC_RUNNING); + if (!guard.was_toggled()) + co_return; + WandService wand_service; if (!wand_service.start(_glasses_handle)) @@ -333,7 +409,7 @@ CotaskPtr Glasses::monitor_wands() { co_return; } - while (_glasses_handle && _state.is_current(GlassesState::SUSTAIN_CONNECTION) && wand_service.is_running()) { + while (_glasses_handle && _state.is_current(GlassesState::READY | GlassesState::SUSTAIN_CONNECTION) && wand_service.is_running()) { auto err = wand_service.get_last_error(); if (err != T5_SUCCESS) { LOG_T5_ERROR(err); @@ -414,6 +490,11 @@ void Glasses::connect(const std::string_view application_name) { } void Glasses::disconnect() { + release_glasses(); + _state.clear(GlassesState::SUSTAIN_CONNECTION); +} + +void Glasses::release_glasses() { if (_state.is_current(GlassesState::READY)) { T5_Result result; { @@ -424,19 +505,19 @@ void Glasses::disconnect() { LOG_T5_ERROR(result); } } - _state.clear(GlassesState::READY | GlassesState::GRAPHICS_INIT | GlassesState::SUSTAIN_CONNECTION); + _state.clear(GlassesState::CONNECTED | GlassesState::READY | GlassesState::GRAPHICS_INIT); on_glasses_released(); } -void Glasses::start_display() { - if (_state.set_and_was_toggled(GlassesState::DISPLAY_STARTED)) { - on_start_display(); +void Glasses::alloc_render_textures() { + if (_state.set_and_was_toggled(GlassesState::TEXTURES_ALLOCATED)) { + on_allocate_render_textures(); } } -void Glasses::stop_display() { - if (_state.clear_and_was_toggled(GlassesState::DISPLAY_STARTED)) { - on_stop_display(); +void Glasses::dealloc_render_textures() { + if (_state.clear_and_was_toggled(GlassesState::TEXTURES_ALLOCATED)) { + on_deallocate_render_textures(); } } @@ -477,7 +558,7 @@ void Glasses::update_pose() { if (result == T5_ERROR_TRY_AGAIN) return; else if (result == T5_ERROR_NOT_CONNECTED) { - _state.clear(GlassesState::CONNECTED); + _state.clear(GlassesState::CONNECTED | GlassesState::READY); LOG_T5_ERROR(result); } else { LOG_T5_ERROR(result); @@ -512,7 +593,7 @@ void Glasses::get_eye_position(Eye eye, T5_Vec3& pos) { } void Glasses::send_frame() { - if (_state.is_current(GlassesState::TRACKING | GlassesState::CONNECTED)) { + if (_state.is_current(GlassesState::TRACKING | GlassesState::CONNECTED | GlassesState::TEXTURES_ALLOCATED)) { on_send_frame(_current_frame_idx); T5_FrameInfo frameInfo; @@ -552,7 +633,7 @@ void Glasses::send_frame() { return; LOG_T5_ERROR(result); if (result == T5_ERROR_NOT_CONNECTED) { - _state.clear(GlassesState::CONNECTED); + _state.clear(GlassesState::CONNECTED | GlassesState::READY); } // not sure how we might get here else if (result == T5_ERROR_GFX_CONTEXT_INIT_FAIL || result == T5_ERROR_INVALID_GFX_CONTEXT) { @@ -568,9 +649,11 @@ bool Glasses::update_connection() { on_glasses_reserved(); } if (_state.became_clear(_previous_update_state, GlassesState::CONNECTED)) { - stop_display(); on_glasses_dropped(); } + if (_state.became_set(_previous_update_state, GlassesState::UNAVAILABLE)) { + _scheduler->add_task(monitor_unavailable()); + } _previous_update_state.sync_from(_state); return true; @@ -583,10 +666,10 @@ bool Glasses::update_tracking() { } void Glasses::get_events(int index, std::vector& out_events) { - if (_state.became_set(_previous_event_state, GlassesState::CREATED)) { + if (_state.became_set(_previous_event_state, GlassesState::EXISTS)) { out_events.push_back(GlassesEvent(index, GlassesEvent::E_ADDED)); } - if (_state.became_clear(_previous_event_state, GlassesState::CREATED)) { + if (_state.became_clear(_previous_event_state, GlassesState::EXISTS)) { out_events.push_back(GlassesEvent(index, GlassesEvent::E_LOST)); } diff --git a/extension/T5Integration/Glasses.h b/extension/T5Integration/Glasses.h index 90968d9..9196bfb 100644 --- a/extension/T5Integration/Glasses.h +++ b/extension/T5Integration/Glasses.h @@ -10,6 +10,7 @@ namespace T5Integration { using namespace std::chrono_literals; using GlassesFlags = StateFlags; +using MonitorFlags = StateFlags; class T5Service; using TaskSystem::CotaskPtr; using TaskSystem::Scheduler; @@ -22,13 +23,13 @@ namespace GlassesState { const uint16_t GRAPHICS_INIT = 0x00000002; //0000000010 const uint16_t SUSTAIN_CONNECTION = 0x00000004; //0000000100 - const uint16_t CREATED = 0x00000008; //0000001000 + const uint16_t EXISTS = 0x00000008; //0000001000 const uint16_t UNAVAILABLE = 0x00000010; //0000010000 const uint16_t TRACKING = 0x00000020; //0000100000 const uint16_t CONNECTED = 0x00000040; //0001000000 const uint16_t TRACKING_WANDS = 0x00000080; //0010000000 const uint16_t ERROR = 0x00000100; //0100000000 - const uint16_t DISPLAY_STARTED = 0x00000200; //1000000000 + const uint16_t TEXTURES_ALLOCATED = 0x00000200; //1000000000 } // clang-format on @@ -81,16 +82,18 @@ class Glasses { const std::string get_id(); const std::string get_name(); + bool is_existing(); bool is_connected(); bool is_available(); bool is_tracking(); bool allocate_handle(T5_Context context); + void set_existing(bool exists); void destroy_handle(); void connect(const std::string_view application_name); void disconnect(); - void start_display(); - void stop_display(); + void alloc_render_textures(); + void dealloc_render_textures(); float get_ipd(); float get_fov(); @@ -132,8 +135,10 @@ class Glasses { void set_swap_chain_texture_pair(int swap_chain_idx, intptr_t left_eye_handle, intptr_t right_eye_handle); void set_swap_chain_texture_array(int swap_chain_idx, intptr_t array_handle); - virtual void on_start_display() {} - virtual void on_stop_display() {} + void release_glasses(); + + virtual void on_allocate_render_textures() {} + virtual void on_deallocate_render_textures() {} virtual void on_glasses_reserved() {} virtual void on_glasses_released() {} virtual void on_glasses_dropped() {} @@ -148,9 +153,8 @@ class Glasses { CotaskPtr monitor_wands(); CotaskPtr query_ipd(); CotaskPtr query_friendly_name(); + CotaskPtr monitor_unavailable(); - bool reserve(); - bool make_ready(); bool initialize_graphics(); void configure_wand_tracking(); @@ -159,9 +163,6 @@ class Glasses { void get_eye_position(Eye eye, T5_Vec3& pos); - void begin_reserved_state(); - void end_reserved_state(); - private: Scheduler::Ptr _scheduler; T5Math::Ptr _math; @@ -188,6 +189,15 @@ class Glasses { std::chrono::milliseconds _poll_rate_for_connecting = 100ms; std::chrono::milliseconds _poll_rate_for_monitoring = 2s; std::chrono::milliseconds _wait_time_for_wand_IO = 100s; + + std::chrono::steady_clock::time_point _last_time_available_checked; + + const uint8_t CONNECTION_MONITOR_FUNC_RUNNING = 0x01; + const uint8_t PARAMETER_MONITOR_FUNC_RUNNING = 0x02; + const uint8_t WAND_MONITOR_FUNC_RUNNING = 0x04; + const uint8_t UNAVAILABLE_MONITOR_FUNC_RUNNING = 0x08; + + MonitorFlags _async_functions_running; }; inline const std::string Glasses::get_id() { @@ -198,6 +208,10 @@ inline const std::string Glasses::get_name() { return _friendly_name; } +inline bool Glasses::is_existing() { + return _state.is_current(GlassesState::EXISTS); +} + inline bool Glasses::is_connected() { return _state.is_current(GlassesState::CONNECTED); } diff --git a/extension/T5Integration/Logging.h b/extension/T5Integration/Logging.h index 3e22072..38f88bf 100644 --- a/extension/T5Integration/Logging.h +++ b/extension/T5Integration/Logging.h @@ -101,3 +101,19 @@ void log_message(T var1, Types... var2) { } \ } #endif + +struct __LogScope { + const char* function; + int line; + __LogScope(const char* func, int l) : + function(func), line(l) { + T5Integration::log_message("===> Entering ", function, " : ", line); + } + ~__LogScope() { + T5Integration::log_message("<=== Leaving ", function, " : ", line); + } +}; + +#ifndef LOG_SCOPE +#define LOG_SCOPE __LogScope __log_scope_instance##__LINE__(__func__, __LINE__); +#endif diff --git a/extension/T5Integration/StateFlags.h b/extension/T5Integration/StateFlags.h index 232c3d2..f8046f2 100644 --- a/extension/T5Integration/StateFlags.h +++ b/extension/T5Integration/StateFlags.h @@ -69,6 +69,31 @@ class StateFlags { _current.store(from._current.load()); } + class ScopeGuard final { + public: + ScopeGuard(const ScopeGuard&) = delete; + ScopeGuard& operator=(const ScopeGuard&) = delete; + ScopeGuard(ScopeGuard&&) = delete; + ScopeGuard& operator=(ScopeGuard&&) = delete; + + ScopeGuard(StateFlags& flags, FlagType state) : + _flags(flags), _state(state) { + _was_toggled = _flags.set_and_was_toggled(_state); + } + ~ScopeGuard() { + if (_was_toggled) + _flags.clear(_state); + } + bool was_toggled() const { + return _was_toggled; + } + + private: + StateFlags& _flags; + FlagType _state; + bool _was_toggled = false; + }; + private: std::atomic _current; }; diff --git a/extension/T5Integration/T5Service.cpp b/extension/T5Integration/T5Service.cpp index 2cb8874..9f9a365 100644 --- a/extension/T5Integration/T5Service.cpp +++ b/extension/T5Integration/T5Service.cpp @@ -54,7 +54,7 @@ void T5Service::stop_service() { _state.clear_and_was_toggled(T5ServiceState::STARTING)) { _scheduler->stop(); for (int i = 0; i < _glasses_list.size(); i++) { - _glasses_list[i]->stop_display(); + _glasses_list[i]->dealloc_render_textures(); _glasses_list[i]->disconnect(); _glasses_list[i]->destroy_handle(); } @@ -157,6 +157,7 @@ CotaskPtr T5Service::query_glasses_list() { buffer.resize(64); T5_Result result; bool first_resize = true; + std::vector failed_exists_checks; for (;;) { size_t bufferSize = buffer.size(); @@ -190,6 +191,9 @@ CotaskPtr T5Service::query_glasses_list() { str_view.remove_prefix(pos + 1); } + for (auto& count : failed_exists_checks) + count++; + co_await run_in_foreground; for (auto& id : parsed_id_list) { @@ -200,8 +204,23 @@ CotaskPtr T5Service::query_glasses_list() { if (found == _glasses_list.cend()) { auto new_glasses = create_glasses(id); - if (new_glasses->allocate_handle(_context)) + if (new_glasses->allocate_handle(_context)) { + LOG_MESSAGE("Found new glasses: ", id); + new_glasses->set_existing(true); _glasses_list.emplace_back(std::move(new_glasses)); + failed_exists_checks.push_back(0); + } + } else { + int index = std::distance(_glasses_list.cbegin(), found); + if (!_glasses_list[index]->is_existing()) { + _glasses_list[index]->set_existing(true); + } + failed_exists_checks[index] = 0; + } + } + for (int i = 0; i < failed_exists_checks.size(); ++i) { + if (failed_exists_checks[i] * _poll_rate_for_monitoring > 30s) { + _glasses_list[i]->set_existing(false); } } diff --git a/extension/src/OpenGLGlasses.cpp b/extension/src/OpenGLGlasses.cpp index bd0b08b..279bf71 100644 --- a/extension/src/OpenGLGlasses.cpp +++ b/extension/src/OpenGLGlasses.cpp @@ -61,11 +61,11 @@ void OpenGLGlasses::deallocate_textures() { } } -void OpenGLGlasses::on_start_display() { +void OpenGLGlasses::on_allocate_render_textures() { allocate_textures(); } -void OpenGLGlasses::on_stop_display() { +void OpenGLGlasses::on_deallocate_render_textures() { deallocate_textures(); } diff --git a/extension/src/OpenGLGlasses.h b/extension/src/OpenGLGlasses.h index c83466e..7a85343 100644 --- a/extension/src/OpenGLGlasses.h +++ b/extension/src/OpenGLGlasses.h @@ -28,8 +28,8 @@ class OpenGLGlasses : public GodotT5Glasses { void allocate_textures(); void deallocate_textures(); - virtual void on_start_display() override; - virtual void on_stop_display() override; + virtual void on_allocate_render_textures() override; + virtual void on_deallocate_render_textures() override; private: std::vector _swap_chain_textures; diff --git a/extension/src/TiltFiveXRInterface.cpp b/extension/src/TiltFiveXRInterface.cpp index 041bec9..9aad7ba 100644 --- a/extension/src/TiltFiveXRInterface.cpp +++ b/extension/src/TiltFiveXRInterface.cpp @@ -223,7 +223,7 @@ void TiltFiveXRInterface::_start_display(TiltFiveXRInterface::GlassesIndexEntry& WARN_PRINT("Glasses need to be reserved to display viewport"); return; } - glasses->start_display(); + glasses->alloc_render_textures(); entry.viewport_id = viewport->get_instance_id(); entry.gameboard_id = gameboard->get_instance_id(); @@ -244,7 +244,7 @@ void TiltFiveXRInterface::_stop_display(GlassesIndexEntry& entry) { viewport->set_use_xr(false); viewport->set_update_mode(godot::SubViewport::UpdateMode::UPDATE_DISABLED); } - glasses->stop_display(); + glasses->dealloc_render_textures(); entry.viewport_id = ObjectID(); entry.gameboard_id = ObjectID(); } @@ -286,7 +286,7 @@ PackedStringArray TiltFiveXRInterface::get_reserved_glasses_ids() { } String TiltFiveXRInterface::get_glasses_name(const StringName glasses_id) { - if(!t5_service) + if (!t5_service) return String(""); auto entry = lookup_glasses_entry(glasses_id); @@ -554,10 +554,9 @@ void TiltFiveXRInterface::_process() { auto glasses_idx = _glasses_events[i].glasses_num; switch (_glasses_events[i].event) { case GlassesEvent::E_ADDED: { - if (_glasses_index.size() != glasses_idx) { - WARN_PRINT("Glasses index"); + if (_glasses_index.size() <= glasses_idx) { + _glasses_index.resize(glasses_idx + 1); } - _glasses_index.resize(glasses_idx + 1); auto glasses = t5_service->get_glasses(glasses_idx); glasses->set_trigger_click_threshold(_trigger_click_threshold); diff --git a/extension/src/VulkanGlasses.cpp b/extension/src/VulkanGlasses.cpp index fe5df89..a7f3723 100644 --- a/extension/src/VulkanGlasses.cpp +++ b/extension/src/VulkanGlasses.cpp @@ -103,11 +103,11 @@ void VulkanGlasses::deallocate_textures() { } } -void VulkanGlasses::on_start_display() { +void VulkanGlasses::on_allocate_render_textures() { allocate_textures(); } -void VulkanGlasses::on_stop_display() { +void VulkanGlasses::on_deallocate_render_textures() { deallocate_textures(); } diff --git a/extension/src/VulkanGlasses.h b/extension/src/VulkanGlasses.h index 5fbce04..dbc8d39 100644 --- a/extension/src/VulkanGlasses.h +++ b/extension/src/VulkanGlasses.h @@ -29,8 +29,8 @@ class VulkanGlasses : public GodotT5Glasses { void allocate_textures(); void deallocate_textures(); - virtual void on_start_display() override; - virtual void on_stop_display() override; + virtual void on_allocate_render_textures() override; + virtual void on_deallocate_render_textures() override; private: std::vector _swap_chain_textures;