Fix crash on shutdown of SteamVR

This commit is contained in:
Brad Davis 2016-05-27 13:39:30 -07:00
parent 76940bad34
commit ac5912df01
13 changed files with 101 additions and 23 deletions

View file

@ -1503,7 +1503,13 @@ void Application::paintGL() {
// FIXME not needed anymore? // FIXME not needed anymore?
_offscreenContext->makeCurrent(); _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 // update the avatar with a fresh HMD pose
getMyAvatar()->updateFromHMDSensorMatrix(getHMDSensorPose()); getMyAvatar()->updateFromHMDSensorMatrix(getHMDSensorPose());
@ -5098,9 +5104,17 @@ void Application::updateDisplayMode() {
foreach(auto displayPlugin, standard) { foreach(auto displayPlugin, standard) {
addDisplayPluginToMenu(displayPlugin, first); addDisplayPluginToMenu(displayPlugin, first);
auto displayPluginName = displayPlugin->getName();
QObject::connect(displayPlugin.get(), &DisplayPlugin::recommendedFramebufferSizeChanged, [this](const QSize & size) { QObject::connect(displayPlugin.get(), &DisplayPlugin::recommendedFramebufferSizeChanged, [this](const QSize & size) {
resizeGL(); 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; first = false;
} }
@ -5116,6 +5130,10 @@ void Application::updateDisplayMode() {
foreach(DisplayPluginPointer displayPlugin, PluginManager::getInstance()->getDisplayPlugins()) { foreach(DisplayPluginPointer displayPlugin, PluginManager::getInstance()->getDisplayPlugins()) {
QString name = displayPlugin->getName(); QString name = displayPlugin->getName();
QAction* action = menu->getActionForOption(name); QAction* action = menu->getActionForOption(name);
// Menu might have been removed if the display plugin lost
if (!action) {
continue;
}
if (action->isChecked()) { if (action->isChecked()) {
newDisplayPlugin = displayPlugin; newDisplayPlugin = displayPlugin;
break; break;

View file

@ -137,7 +137,7 @@ public:
} }
// will query the underlying hmd api to compute the most recent head pose // 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 // returns a copy of the most recent head pose, computed via updateHeadPose
virtual glm::mat4 getHeadPose() const { virtual glm::mat4 getHeadPose() const {
@ -170,6 +170,10 @@ public:
signals: signals:
void recommendedFramebufferSizeChanged(const QSize & size); 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: protected:
void incrementPresentCount(); void incrementPresentCount();

View file

@ -62,11 +62,10 @@ PluginManager::PluginManager() {
extern DisplayPluginList getDisplayPlugins(); extern DisplayPluginList getDisplayPlugins();
extern InputPluginList getInputPlugins(); extern InputPluginList getInputPlugins();
extern void saveInputPluginSettings(const InputPluginList& plugins); extern void saveInputPluginSettings(const InputPluginList& plugins);
static DisplayPluginList displayPlugins;
const DisplayPluginList& PluginManager::getDisplayPlugins() { const DisplayPluginList& PluginManager::getDisplayPlugins() {
static DisplayPluginList displayPlugins;
static std::once_flag once; static std::once_flag once;
std::call_once(once, [&] { std::call_once(once, [&] {
// Grab the built in plugins // Grab the built in plugins
displayPlugins = ::getDisplayPlugins(); displayPlugins = ::getDisplayPlugins();
@ -90,6 +89,16 @@ const DisplayPluginList& PluginManager::getDisplayPlugins() {
return displayPlugins; 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() { const InputPluginList& PluginManager::getInputPlugins() {
static InputPluginList inputPlugins; static InputPluginList inputPlugins;
static std::once_flag once; static std::once_flag once;

View file

@ -17,6 +17,7 @@ public:
PluginManager(); PluginManager();
const DisplayPluginList& getDisplayPlugins(); const DisplayPluginList& getDisplayPlugins();
void disableDisplayPlugin(const QString& name);
const InputPluginList& getInputPlugins(); const InputPluginList& getInputPlugins();
void saveSettings(); void saveSettings();
}; };

View file

@ -17,7 +17,7 @@ void OculusBaseDisplayPlugin::resetSensors() {
_currentRenderFrameInfo.renderPose = glm::mat4(); // identity _currentRenderFrameInfo.renderPose = glm::mat4(); // identity
} }
void OculusBaseDisplayPlugin::beginFrameRender(uint32_t frameIndex) { bool OculusBaseDisplayPlugin::beginFrameRender(uint32_t frameIndex) {
_currentRenderFrameInfo = FrameInfo(); _currentRenderFrameInfo = FrameInfo();
_currentRenderFrameInfo.sensorSampleTime = ovr_GetTimeInSeconds();; _currentRenderFrameInfo.sensorSampleTime = ovr_GetTimeInSeconds();;
_currentRenderFrameInfo.predictedDisplayTime = ovr_GetPredictedDisplayTime(_session, frameIndex); _currentRenderFrameInfo.predictedDisplayTime = ovr_GetPredictedDisplayTime(_session, frameIndex);
@ -26,6 +26,7 @@ void OculusBaseDisplayPlugin::beginFrameRender(uint32_t frameIndex) {
_currentRenderFrameInfo.presentPose = _currentRenderFrameInfo.renderPose; _currentRenderFrameInfo.presentPose = _currentRenderFrameInfo.renderPose;
Lock lock(_mutex); Lock lock(_mutex);
_frameInfos[frameIndex] = _currentRenderFrameInfo; _frameInfos[frameIndex] = _currentRenderFrameInfo;
return true;
} }
bool OculusBaseDisplayPlugin::isSupported() const { bool OculusBaseDisplayPlugin::isSupported() const {

View file

@ -16,11 +16,11 @@
class OculusBaseDisplayPlugin : public HmdDisplayPlugin { class OculusBaseDisplayPlugin : public HmdDisplayPlugin {
using Parent = HmdDisplayPlugin; using Parent = HmdDisplayPlugin;
public: public:
virtual bool isSupported() const override; bool isSupported() const override;
// Stereo specific methods // Stereo specific methods
virtual void resetSensors() override final; void resetSensors() override final;
virtual void beginFrameRender(uint32_t frameIndex) override; bool beginFrameRender(uint32_t frameIndex) override;
float getTargetFrameRate() const override { return _hmdDesc.DisplayRefreshRate; } float getTargetFrameRate() const override { return _hmdDesc.DisplayRefreshRate; }

View file

@ -40,13 +40,14 @@ void OculusLegacyDisplayPlugin::resetSensors() {
ovrHmd_RecenterPose(_hmd); ovrHmd_RecenterPose(_hmd);
} }
void OculusLegacyDisplayPlugin::beginFrameRender(uint32_t frameIndex) { bool OculusLegacyDisplayPlugin::beginFrameRender(uint32_t frameIndex) {
_currentRenderFrameInfo = FrameInfo(); _currentRenderFrameInfo = FrameInfo();
_currentRenderFrameInfo.predictedDisplayTime = _currentRenderFrameInfo.sensorSampleTime = ovr_GetTimeInSeconds(); _currentRenderFrameInfo.predictedDisplayTime = _currentRenderFrameInfo.sensorSampleTime = ovr_GetTimeInSeconds();
_trackingState = ovrHmd_GetTrackingState(_hmd, _currentRenderFrameInfo.predictedDisplayTime); _trackingState = ovrHmd_GetTrackingState(_hmd, _currentRenderFrameInfo.predictedDisplayTime);
_currentRenderFrameInfo.rawRenderPose = _currentRenderFrameInfo.renderPose = toGlm(_trackingState.HeadPose.ThePose); _currentRenderFrameInfo.rawRenderPose = _currentRenderFrameInfo.renderPose = toGlm(_trackingState.HeadPose.ThePose);
Lock lock(_mutex); Lock lock(_mutex);
_frameInfos[frameIndex] = _currentRenderFrameInfo; _frameInfos[frameIndex] = _currentRenderFrameInfo;
return true;
} }
bool OculusLegacyDisplayPlugin::isSupported() const { bool OculusLegacyDisplayPlugin::isSupported() const {

View file

@ -27,7 +27,7 @@ public:
// Stereo specific methods // Stereo specific methods
void resetSensors() override; void resetSensors() override;
void beginFrameRender(uint32_t frameIndex) override; bool beginFrameRender(uint32_t frameIndex) override;
float getTargetFrameRate() const override; float getTargetFrameRate() const override;

View file

@ -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 displayFrequency = _system->GetFloatTrackedDeviceProperty(vr::k_unTrackedDeviceIndex_Hmd, vr::Prop_DisplayFrequency_Float);
double frameDuration = 1.f / displayFrequency; double frameDuration = 1.f / displayFrequency;
double vsyncToPhotons = _system->GetFloatTrackedDeviceProperty(vr::k_unTrackedDeviceIndex_Hmd, vr::Prop_SecondsFromVsyncToPhotons_Float); 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); Lock lock(_mutex);
_frameInfos[frameIndex] = _currentRenderFrameInfo; _frameInfos[frameIndex] = _currentRenderFrameInfo;
return true;
} }
void OpenVrDisplayPlugin::hmdPresent() { void OpenVrDisplayPlugin::hmdPresent() {

View file

@ -18,16 +18,16 @@ const float TARGET_RATE_OpenVr = 90.0f; // FIXME: get from sdk tracked device p
class OpenVrDisplayPlugin : public HmdDisplayPlugin { class OpenVrDisplayPlugin : public HmdDisplayPlugin {
using Parent = HmdDisplayPlugin; using Parent = HmdDisplayPlugin;
public: public:
virtual bool isSupported() const override; bool isSupported() const override;
virtual const QString& getName() const override { return NAME; } 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 // Stereo specific methods
virtual void resetSensors() override; void resetSensors() override;
virtual void beginFrameRender(uint32_t frameIndex) override; bool beginFrameRender(uint32_t frameIndex) override;
void cycleDebugOutput() override { _lockCurrentTexture = !_lockCurrentTexture; } void cycleDebugOutput() override { _lockCurrentTexture = !_lockCurrentTexture; }
protected: protected:

View file

@ -26,6 +26,11 @@ using Lock = std::unique_lock<Mutex>;
static int refCount { 0 }; static int refCount { 0 };
static Mutex mutex; static Mutex mutex;
static vr::IVRSystem* activeHmd { nullptr }; static vr::IVRSystem* activeHmd { nullptr };
static bool _openVrQuitRequested { false };
bool openVrQuitRequested() {
return _openVrQuitRequested;
}
static const uint32_t RELEASE_OPENVR_HMD_DELAY_MS = 5000; static const uint32_t RELEASE_OPENVR_HMD_DELAY_MS = 5000;
@ -56,17 +61,17 @@ vr::IVRSystem* acquireOpenVrSystem() {
if (hmdPresent) { if (hmdPresent) {
Lock lock(mutex); Lock lock(mutex);
if (!activeHmd) { 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; vr::EVRInitError eError = vr::VRInitError_None;
activeHmd = vr::VR_Init(&eError, vr::VRApplication_Scene); 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) { if (activeHmd) {
qCDebug(displayplugins) << "openvr: incrementing refcount"; qCDebug(displayplugins) << "OpenVR: incrementing refcount";
++refCount; ++refCount;
} }
} else { } else {
qCDebug(displayplugins) << "openvr: no hmd present"; qCDebug(displayplugins) << "OpenVR: no hmd present";
} }
return activeHmd; return activeHmd;
} }
@ -74,12 +79,38 @@ vr::IVRSystem* acquireOpenVrSystem() {
void releaseOpenVrSystem() { void releaseOpenVrSystem() {
if (activeHmd) { if (activeHmd) {
Lock lock(mutex); Lock lock(mutex);
qCDebug(displayplugins) << "openvr: decrementing refcount"; qCDebug(displayplugins) << "OpenVR: decrementing refcount";
--refCount; --refCount;
if (0 == refCount) { if (0 == refCount) {
qCDebug(displayplugins) << "openvr: zero refcount, deallocate VR system"; qCDebug(displayplugins) << "OpenVR: zero refcount, deallocate VR system";
vr::VR_Shutdown(); vr::VR_Shutdown();
_openVrQuitRequested = false;
activeHmd = nullptr; 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;
}
}

View file

@ -16,6 +16,8 @@ bool openVrSupported();
vr::IVRSystem* acquireOpenVrSystem(); vr::IVRSystem* acquireOpenVrSystem();
void releaseOpenVrSystem(); void releaseOpenVrSystem();
void handleOpenVrEvents();
bool openVrQuitRequested();
template<typename F> template<typename F>
void openvr_for_each_eye(F f) { void openvr_for_each_eye(F f) {

View file

@ -214,6 +214,11 @@ void ViveControllerManager::renderHand(const controller::Pose& pose, gpu::Batch&
void ViveControllerManager::pluginUpdate(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) { void ViveControllerManager::pluginUpdate(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) {
auto userInputMapper = DependencyManager::get<controller::UserInputMapper>(); auto userInputMapper = DependencyManager::get<controller::UserInputMapper>();
handleOpenVrEvents();
if (openVrQuitRequested()) {
deactivate();
return;
}
// because update mutates the internal state we need to lock // because update mutates the internal state we need to lock
userInputMapper->withLock([&, this]() { userInputMapper->withLock([&, this]() {