diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 48b418b93c..f2d4f9edf1 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1503,7 +1503,13 @@ void Application::paintGL() { // FIXME not needed anymore? _offscreenContext->makeCurrent(); - displayPlugin->beginFrameRender(_frameCount); + // If a display plugin loses it's underlying support, it + // needs to be able to signal us to not use it + if (!displayPlugin->beginFrameRender(_frameCount)) { + _inPaint = false; + updateDisplayMode(); + return; + } // update the avatar with a fresh HMD pose getMyAvatar()->updateFromHMDSensorMatrix(getHMDSensorPose()); @@ -5098,9 +5104,17 @@ void Application::updateDisplayMode() { foreach(auto displayPlugin, standard) { addDisplayPluginToMenu(displayPlugin, first); + auto displayPluginName = displayPlugin->getName(); QObject::connect(displayPlugin.get(), &DisplayPlugin::recommendedFramebufferSizeChanged, [this](const QSize & size) { resizeGL(); }); + QObject::connect(displayPlugin.get(), &DisplayPlugin::outputDeviceLost, [this, displayPluginName] { + PluginManager::getInstance()->disableDisplayPlugin(displayPluginName); + auto menu = Menu::getInstance(); + if (menu->menuItemExists(MenuOption::OutputMenu, displayPluginName)) { + menu->removeMenuItem(MenuOption::OutputMenu, displayPluginName); + } + }); first = false; } @@ -5116,6 +5130,10 @@ void Application::updateDisplayMode() { foreach(DisplayPluginPointer displayPlugin, PluginManager::getInstance()->getDisplayPlugins()) { QString name = displayPlugin->getName(); QAction* action = menu->getActionForOption(name); + // Menu might have been removed if the display plugin lost + if (!action) { + continue; + } if (action->isChecked()) { newDisplayPlugin = displayPlugin; break; diff --git a/libraries/plugins/src/plugins/DisplayPlugin.h b/libraries/plugins/src/plugins/DisplayPlugin.h index 41f380aa86..1f6a16cd46 100644 --- a/libraries/plugins/src/plugins/DisplayPlugin.h +++ b/libraries/plugins/src/plugins/DisplayPlugin.h @@ -137,7 +137,7 @@ public: } // will query the underlying hmd api to compute the most recent head pose - virtual void beginFrameRender(uint32_t frameIndex) {} + virtual bool beginFrameRender(uint32_t frameIndex) { return true; } // returns a copy of the most recent head pose, computed via updateHeadPose virtual glm::mat4 getHeadPose() const { @@ -170,6 +170,10 @@ public: signals: void recommendedFramebufferSizeChanged(const QSize & size); + // Indicates that this display plugin is no longer valid for use. + // For instance if a user exits Oculus Home or Steam VR while + // using the corresponding plugin, that plugin should be disabled. + void outputDeviceLost(); protected: void incrementPresentCount(); diff --git a/libraries/plugins/src/plugins/PluginManager.cpp b/libraries/plugins/src/plugins/PluginManager.cpp index 936cb8a7ee..eb6465aab2 100644 --- a/libraries/plugins/src/plugins/PluginManager.cpp +++ b/libraries/plugins/src/plugins/PluginManager.cpp @@ -62,11 +62,10 @@ PluginManager::PluginManager() { extern DisplayPluginList getDisplayPlugins(); extern InputPluginList getInputPlugins(); extern void saveInputPluginSettings(const InputPluginList& plugins); +static DisplayPluginList displayPlugins; const DisplayPluginList& PluginManager::getDisplayPlugins() { - static DisplayPluginList displayPlugins; static std::once_flag once; - std::call_once(once, [&] { // Grab the built in plugins displayPlugins = ::getDisplayPlugins(); @@ -90,6 +89,16 @@ const DisplayPluginList& PluginManager::getDisplayPlugins() { return displayPlugins; } +void PluginManager::disableDisplayPlugin(const QString& name) { + for (size_t i = 0; i < displayPlugins.size(); ++i) { + if (displayPlugins[i]->getName() == name) { + displayPlugins.erase(displayPlugins.begin() + i); + break; + } + } +} + + const InputPluginList& PluginManager::getInputPlugins() { static InputPluginList inputPlugins; static std::once_flag once; diff --git a/libraries/plugins/src/plugins/PluginManager.h b/libraries/plugins/src/plugins/PluginManager.h index 2e056414ec..cf0b8efe64 100644 --- a/libraries/plugins/src/plugins/PluginManager.h +++ b/libraries/plugins/src/plugins/PluginManager.h @@ -17,6 +17,7 @@ public: PluginManager(); const DisplayPluginList& getDisplayPlugins(); + void disableDisplayPlugin(const QString& name); const InputPluginList& getInputPlugins(); void saveSettings(); }; diff --git a/plugins/oculus/src/OculusBaseDisplayPlugin.cpp b/plugins/oculus/src/OculusBaseDisplayPlugin.cpp index 3b6545ae96..e9f8545cff 100644 --- a/plugins/oculus/src/OculusBaseDisplayPlugin.cpp +++ b/plugins/oculus/src/OculusBaseDisplayPlugin.cpp @@ -17,7 +17,7 @@ void OculusBaseDisplayPlugin::resetSensors() { _currentRenderFrameInfo.renderPose = glm::mat4(); // identity } -void OculusBaseDisplayPlugin::beginFrameRender(uint32_t frameIndex) { +bool OculusBaseDisplayPlugin::beginFrameRender(uint32_t frameIndex) { _currentRenderFrameInfo = FrameInfo(); _currentRenderFrameInfo.sensorSampleTime = ovr_GetTimeInSeconds();; _currentRenderFrameInfo.predictedDisplayTime = ovr_GetPredictedDisplayTime(_session, frameIndex); @@ -26,6 +26,7 @@ void OculusBaseDisplayPlugin::beginFrameRender(uint32_t frameIndex) { _currentRenderFrameInfo.presentPose = _currentRenderFrameInfo.renderPose; Lock lock(_mutex); _frameInfos[frameIndex] = _currentRenderFrameInfo; + return true; } bool OculusBaseDisplayPlugin::isSupported() const { diff --git a/plugins/oculus/src/OculusBaseDisplayPlugin.h b/plugins/oculus/src/OculusBaseDisplayPlugin.h index ed6f1a36b3..3e2c223908 100644 --- a/plugins/oculus/src/OculusBaseDisplayPlugin.h +++ b/plugins/oculus/src/OculusBaseDisplayPlugin.h @@ -16,11 +16,11 @@ class OculusBaseDisplayPlugin : public HmdDisplayPlugin { using Parent = HmdDisplayPlugin; public: - virtual bool isSupported() const override; + bool isSupported() const override; // Stereo specific methods - virtual void resetSensors() override final; - virtual void beginFrameRender(uint32_t frameIndex) override; + void resetSensors() override final; + bool beginFrameRender(uint32_t frameIndex) override; float getTargetFrameRate() const override { return _hmdDesc.DisplayRefreshRate; } diff --git a/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp b/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp index 753ff923dd..f89e71b829 100644 --- a/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp +++ b/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp @@ -40,13 +40,14 @@ void OculusLegacyDisplayPlugin::resetSensors() { ovrHmd_RecenterPose(_hmd); } -void OculusLegacyDisplayPlugin::beginFrameRender(uint32_t frameIndex) { +bool OculusLegacyDisplayPlugin::beginFrameRender(uint32_t frameIndex) { _currentRenderFrameInfo = FrameInfo(); _currentRenderFrameInfo.predictedDisplayTime = _currentRenderFrameInfo.sensorSampleTime = ovr_GetTimeInSeconds(); _trackingState = ovrHmd_GetTrackingState(_hmd, _currentRenderFrameInfo.predictedDisplayTime); _currentRenderFrameInfo.rawRenderPose = _currentRenderFrameInfo.renderPose = toGlm(_trackingState.HeadPose.ThePose); Lock lock(_mutex); _frameInfos[frameIndex] = _currentRenderFrameInfo; + return true; } bool OculusLegacyDisplayPlugin::isSupported() const { diff --git a/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.h b/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.h index 5900ad4c58..453a6f9168 100644 --- a/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.h +++ b/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.h @@ -27,7 +27,7 @@ public: // Stereo specific methods void resetSensors() override; - void beginFrameRender(uint32_t frameIndex) override; + bool beginFrameRender(uint32_t frameIndex) override; float getTargetFrameRate() const override; diff --git a/plugins/openvr/src/OpenVrDisplayPlugin.cpp b/plugins/openvr/src/OpenVrDisplayPlugin.cpp index dba8fca208..2e0bebeadd 100644 --- a/plugins/openvr/src/OpenVrDisplayPlugin.cpp +++ b/plugins/openvr/src/OpenVrDisplayPlugin.cpp @@ -121,7 +121,12 @@ void OpenVrDisplayPlugin::resetSensors() { } -void OpenVrDisplayPlugin::beginFrameRender(uint32_t frameIndex) { +bool OpenVrDisplayPlugin::beginFrameRender(uint32_t frameIndex) { + handleOpenVrEvents(); + if (openVrQuitRequested()) { + emit outputDeviceLost(); + return false; + } double displayFrequency = _system->GetFloatTrackedDeviceProperty(vr::k_unTrackedDeviceIndex_Hmd, vr::Prop_DisplayFrequency_Float); double frameDuration = 1.f / displayFrequency; double vsyncToPhotons = _system->GetFloatTrackedDeviceProperty(vr::k_unTrackedDeviceIndex_Hmd, vr::Prop_SecondsFromVsyncToPhotons_Float); @@ -148,6 +153,7 @@ void OpenVrDisplayPlugin::beginFrameRender(uint32_t frameIndex) { Lock lock(_mutex); _frameInfos[frameIndex] = _currentRenderFrameInfo; + return true; } void OpenVrDisplayPlugin::hmdPresent() { diff --git a/plugins/openvr/src/OpenVrDisplayPlugin.h b/plugins/openvr/src/OpenVrDisplayPlugin.h index 75193c5c98..fda5e37c2a 100644 --- a/plugins/openvr/src/OpenVrDisplayPlugin.h +++ b/plugins/openvr/src/OpenVrDisplayPlugin.h @@ -18,16 +18,16 @@ const float TARGET_RATE_OpenVr = 90.0f; // FIXME: get from sdk tracked device p class OpenVrDisplayPlugin : public HmdDisplayPlugin { using Parent = HmdDisplayPlugin; public: - virtual bool isSupported() const override; - virtual const QString& getName() const override { return NAME; } + bool isSupported() const override; + const QString& getName() const override { return NAME; } - virtual float getTargetFrameRate() const override { return TARGET_RATE_OpenVr; } + float getTargetFrameRate() const override { return TARGET_RATE_OpenVr; } - virtual void customizeContext() override; + void customizeContext() override; // Stereo specific methods - virtual void resetSensors() override; - virtual void beginFrameRender(uint32_t frameIndex) override; + void resetSensors() override; + bool beginFrameRender(uint32_t frameIndex) override; void cycleDebugOutput() override { _lockCurrentTexture = !_lockCurrentTexture; } protected: diff --git a/plugins/openvr/src/OpenVrHelpers.cpp b/plugins/openvr/src/OpenVrHelpers.cpp index 8536ffd5d9..155bc9f079 100644 --- a/plugins/openvr/src/OpenVrHelpers.cpp +++ b/plugins/openvr/src/OpenVrHelpers.cpp @@ -26,6 +26,11 @@ using Lock = std::unique_lock; static int refCount { 0 }; static Mutex mutex; static vr::IVRSystem* activeHmd { nullptr }; +static bool _openVrQuitRequested { false }; + +bool openVrQuitRequested() { + return _openVrQuitRequested; +} static const uint32_t RELEASE_OPENVR_HMD_DELAY_MS = 5000; @@ -56,17 +61,17 @@ vr::IVRSystem* acquireOpenVrSystem() { if (hmdPresent) { Lock lock(mutex); if (!activeHmd) { - qCDebug(displayplugins) << "openvr: No vr::IVRSystem instance active, building"; + qCDebug(displayplugins) << "OpenVR: No vr::IVRSystem instance active, building"; vr::EVRInitError eError = vr::VRInitError_None; activeHmd = vr::VR_Init(&eError, vr::VRApplication_Scene); - qCDebug(displayplugins) << "openvr display: HMD is " << activeHmd << " error is " << eError; + qCDebug(displayplugins) << "OpenVR display: HMD is " << activeHmd << " error is " << eError; } if (activeHmd) { - qCDebug(displayplugins) << "openvr: incrementing refcount"; + qCDebug(displayplugins) << "OpenVR: incrementing refcount"; ++refCount; } } else { - qCDebug(displayplugins) << "openvr: no hmd present"; + qCDebug(displayplugins) << "OpenVR: no hmd present"; } return activeHmd; } @@ -74,12 +79,38 @@ vr::IVRSystem* acquireOpenVrSystem() { void releaseOpenVrSystem() { if (activeHmd) { Lock lock(mutex); - qCDebug(displayplugins) << "openvr: decrementing refcount"; + qCDebug(displayplugins) << "OpenVR: decrementing refcount"; --refCount; if (0 == refCount) { - qCDebug(displayplugins) << "openvr: zero refcount, deallocate VR system"; + qCDebug(displayplugins) << "OpenVR: zero refcount, deallocate VR system"; vr::VR_Shutdown(); + _openVrQuitRequested = false; activeHmd = nullptr; } } } + +void handleOpenVrEvents() { + if (!activeHmd) { + return; + } + Lock lock(mutex); + if (!activeHmd) { + return; + } + + vr::VREvent_t event; + while (activeHmd->PollNextEvent(&event, sizeof(event))) { + switch (event.eventType) { + case vr::VREvent_Quit: + _openVrQuitRequested = true; + activeHmd->AcknowledgeQuit_Exiting(); + break; + + default: + break; + } + qDebug() << "OpenVR: Event " << event.eventType; + } + +} diff --git a/plugins/openvr/src/OpenVrHelpers.h b/plugins/openvr/src/OpenVrHelpers.h index 4b06ca0813..1e5914844c 100644 --- a/plugins/openvr/src/OpenVrHelpers.h +++ b/plugins/openvr/src/OpenVrHelpers.h @@ -16,6 +16,8 @@ bool openVrSupported(); vr::IVRSystem* acquireOpenVrSystem(); void releaseOpenVrSystem(); +void handleOpenVrEvents(); +bool openVrQuitRequested(); template void openvr_for_each_eye(F f) { diff --git a/plugins/openvr/src/ViveControllerManager.cpp b/plugins/openvr/src/ViveControllerManager.cpp index 6e75454b5f..953501ccec 100644 --- a/plugins/openvr/src/ViveControllerManager.cpp +++ b/plugins/openvr/src/ViveControllerManager.cpp @@ -214,6 +214,11 @@ void ViveControllerManager::renderHand(const controller::Pose& pose, gpu::Batch& void ViveControllerManager::pluginUpdate(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) { auto userInputMapper = DependencyManager::get(); + handleOpenVrEvents(); + if (openVrQuitRequested()) { + deactivate(); + return; + } // because update mutates the internal state we need to lock userInputMapper->withLock([&, this]() {