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?
_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;

View file

@ -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();

View file

@ -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;

View file

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

View file

@ -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 {

View file

@ -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; }

View file

@ -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 {

View file

@ -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;

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 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() {

View file

@ -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:

View file

@ -26,6 +26,11 @@ using Lock = std::unique_lock<Mutex>;
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;
}
}

View file

@ -16,6 +16,8 @@ bool openVrSupported();
vr::IVRSystem* acquireOpenVrSystem();
void releaseOpenVrSystem();
void handleOpenVrEvents();
bool openVrQuitRequested();
template<typename 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) {
auto userInputMapper = DependencyManager::get<controller::UserInputMapper>();
handleOpenVrEvents();
if (openVrQuitRequested()) {
deactivate();
return;
}
// because update mutates the internal state we need to lock
userInputMapper->withLock([&, this]() {