Merge pull request #5697 from jherico/maggie

Fix oculus crash on switching display plugin to something else
This commit is contained in:
Brad Hefta-Gaub 2015-09-02 21:23:38 -07:00
commit 01b6bbe40f
11 changed files with 148 additions and 89 deletions

View file

@ -64,6 +64,7 @@
#include <EntityScriptingInterface.h> #include <EntityScriptingInterface.h>
#include <ErrorDialog.h> #include <ErrorDialog.h>
#include <Finally.h>
#include <FramebufferCache.h> #include <FramebufferCache.h>
#include <gpu/Batch.h> #include <gpu/Batch.h>
#include <gpu/Context.h> #include <gpu/Context.h>
@ -1009,6 +1010,16 @@ void Application::paintGL() {
if (nullptr == _displayPlugin) { if (nullptr == _displayPlugin) {
return; return;
} }
// Some plugins process message events, potentially leading to
// re-entering a paint event. don't allow further processing if this
// happens
if (_inPaint) {
return;
}
_inPaint = true;
Finally clearFlagLambda([this] { _inPaint = false; });
auto displayPlugin = getActiveDisplayPlugin(); auto displayPlugin = getActiveDisplayPlugin();
displayPlugin->preRender(); displayPlugin->preRender();
_offscreenContext->makeCurrent(); _offscreenContext->makeCurrent();
@ -1219,7 +1230,6 @@ void Application::paintGL() {
_frameCount++; _frameCount++;
Stats::getInstance()->setRenderDetails(renderArgs._details); Stats::getInstance()->setRenderDetails(renderArgs._details);
// Reset the gpu::Context Stages // Reset the gpu::Context Stages
// Back to the default framebuffer; // Back to the default framebuffer;
gpu::Batch batch; gpu::Batch batch;
@ -4703,7 +4713,23 @@ void Application::updateDisplayMode() {
auto offscreenUi = DependencyManager::get<OffscreenUi>(); auto offscreenUi = DependencyManager::get<OffscreenUi>();
DisplayPluginPointer oldDisplayPlugin = _displayPlugin; DisplayPluginPointer oldDisplayPlugin = _displayPlugin;
if (oldDisplayPlugin != newDisplayPlugin) { if (newDisplayPlugin == oldDisplayPlugin) {
return;
}
// Some plugins *cough* Oculus *cough* process message events from inside their
// display function, and we don't want to change the display plugin underneath
// the paintGL call, so we need to guard against that
if (_inPaint) {
qDebug() << "Deferring plugin switch until out of painting";
// Have the old plugin stop requesting renders
oldDisplayPlugin->stop();
QCoreApplication::postEvent(this, new LambdaEvent([this] {
updateDisplayMode();
}));
return;
}
if (!_currentDisplayPluginActions.isEmpty()) { if (!_currentDisplayPluginActions.isEmpty()) {
auto menu = Menu::getInstance(); auto menu = Menu::getInstance();
foreach(auto itemInfo, _currentDisplayPluginActions) { foreach(auto itemInfo, _currentDisplayPluginActions) {
@ -4749,7 +4775,6 @@ void Application::updateDisplayMode() {
} }
emit activeDisplayPluginChanged(); emit activeDisplayPluginChanged();
resetSensors(); resetSensors();
}
Q_ASSERT_X(_displayPlugin, "Application::updateDisplayMode", "could not find an activated display plugin"); Q_ASSERT_X(_displayPlugin, "Application::updateDisplayMode", "could not find an activated display plugin");
} }
@ -5052,3 +5077,15 @@ void Application::crashApplication() {
qCDebug(interfaceapp) << "Intentionally crashed Interface"; qCDebug(interfaceapp) << "Intentionally crashed Interface";
} }
void Application::setActiveDisplayPlugin(const QString& pluginName) {
auto menu = Menu::getInstance();
foreach(DisplayPluginPointer displayPlugin, PluginManager::getInstance()->getDisplayPlugins()) {
QString name = displayPlugin->getName();
QAction* action = menu->getActionForOption(name);
if (pluginName == name) {
action->setChecked(true);
}
}
updateDisplayMode();
}

View file

@ -673,6 +673,7 @@ private:
int _simsPerSecondReport = 0; int _simsPerSecondReport = 0;
quint64 _lastSimsPerSecondUpdate = 0; quint64 _lastSimsPerSecondUpdate = 0;
bool _isForeground = true; // starts out assumed to be in foreground bool _isForeground = true; // starts out assumed to be in foreground
bool _inPaint = false;
friend class PluginContainerProxy; friend class PluginContainerProxy;
}; };

View file

@ -147,15 +147,3 @@ void PluginContainerProxy::showDisplayPluginsTools() {
QGLWidget* PluginContainerProxy::getPrimarySurface() { QGLWidget* PluginContainerProxy::getPrimarySurface() {
return qApp->_glWidget; return qApp->_glWidget;
} }
void Application::setActiveDisplayPlugin(const QString& pluginName) {
auto menu = Menu::getInstance();
foreach(DisplayPluginPointer displayPlugin, PluginManager::getInstance()->getDisplayPlugins()) {
QString name = displayPlugin->getName();
QAction* action = menu->getActionForOption(name);
if (pluginName == name) {
action->setChecked(true);
}
}
updateDisplayMode();
}

View file

@ -57,6 +57,11 @@ public:
// Rendering support // Rendering support
// Stop requesting renders, but don't do full deactivation
// needed to work around the issues caused by Oculus
// processing messages in the middle of submitFrame
virtual void stop() = 0;
/** /**
* Called by the application before the frame rendering. Can be used for * Called by the application before the frame rendering. Can be used for
* render timing related calls (for instance, the Oculus begin frame timing * render timing related calls (for instance, the Oculus begin frame timing
@ -120,6 +125,7 @@ public:
virtual void resetSensors() {} virtual void resetSensors() {}
virtual float devicePixelRatio() { return 1.0; } virtual float devicePixelRatio() { return 1.0; }
static const QString& MENU_PATH(); static const QString& MENU_PATH();
signals: signals:
void recommendedFramebufferSizeChanged(const QSize & size); void recommendedFramebufferSizeChanged(const QSize & size);

View file

@ -30,3 +30,4 @@ void NullDisplayPlugin::finishFrame() {}
void NullDisplayPlugin::activate() {} void NullDisplayPlugin::activate() {}
void NullDisplayPlugin::deactivate() {} void NullDisplayPlugin::deactivate() {}
void NullDisplayPlugin::stop() {}

View file

@ -17,6 +17,7 @@ public:
void activate() override; void activate() override;
void deactivate() override; void deactivate() override;
void stop() override;
virtual glm::uvec2 getRecommendedRenderSize() const override; virtual glm::uvec2 getRecommendedRenderSize() const override;
virtual bool hasFocus() const override; virtual bool hasFocus() const override;

View file

@ -16,7 +16,9 @@
OpenGLDisplayPlugin::OpenGLDisplayPlugin() { OpenGLDisplayPlugin::OpenGLDisplayPlugin() {
connect(&_timer, &QTimer::timeout, this, [&] { connect(&_timer, &QTimer::timeout, this, [&] {
if (_active) {
emit requestRender(); emit requestRender();
}
}); });
} }
@ -56,10 +58,17 @@ void OpenGLDisplayPlugin::customizeContext() {
} }
void OpenGLDisplayPlugin::activate() { void OpenGLDisplayPlugin::activate() {
_active = true;
_timer.start(1); _timer.start(1);
} }
void OpenGLDisplayPlugin::stop() {
_active = false;
_timer.stop();
}
void OpenGLDisplayPlugin::deactivate() { void OpenGLDisplayPlugin::deactivate() {
_active = false;
_timer.stop(); _timer.stop();
makeCurrent(); makeCurrent();

View file

@ -25,7 +25,7 @@ public:
virtual void activate() override; virtual void activate() override;
virtual void deactivate() override; virtual void deactivate() override;
virtual void stop() override;
virtual bool eventFilter(QObject* receiver, QEvent* event) override; virtual bool eventFilter(QObject* receiver, QEvent* event) override;
virtual void display(GLuint sceneTexture, const glm::uvec2& sceneSize) override; virtual void display(GLuint sceneTexture, const glm::uvec2& sceneSize) override;
@ -44,6 +44,7 @@ protected:
mutable QTimer _timer; mutable QTimer _timer;
ProgramPtr _program; ProgramPtr _program;
ShapeWrapperPtr _plane; ShapeWrapperPtr _plane;
bool _active{ false };
bool _vsyncSupported{ false }; bool _vsyncSupported{ false };
}; };

View file

@ -325,19 +325,18 @@ void OculusDisplayPlugin::customizeContext() {
//_texture = DependencyManager::get<TextureCache>()-> //_texture = DependencyManager::get<TextureCache>()->
// getImageTexture(PathUtils::resourcesPath() + "/images/cube_texture.png"); // getImageTexture(PathUtils::resourcesPath() + "/images/cube_texture.png");
uvec2 mirrorSize = toGlm(_window->geometry().size()); uvec2 mirrorSize = toGlm(_window->geometry().size());
_mirrorFbo = MirrorFboPtr(new MirrorFramebufferWrapper(_hmd));
_mirrorFbo->Init(mirrorSize);
_sceneFbo = SwapFboPtr(new SwapFramebufferWrapper(_hmd)); _sceneFbo = SwapFboPtr(new SwapFramebufferWrapper(_hmd));
_sceneFbo->Init(getRecommendedRenderSize()); _sceneFbo->Init(getRecommendedRenderSize());
#endif #endif
enableVsync(false);
isVsyncEnabled();
} }
void OculusDisplayPlugin::deactivate() { void OculusDisplayPlugin::deactivate() {
#if (OVR_MAJOR_VERSION >= 6) #if (OVR_MAJOR_VERSION >= 6)
makeCurrent(); makeCurrent();
_sceneFbo.reset(); _sceneFbo.reset();
_mirrorFbo.reset();
doneCurrent(); doneCurrent();
PerformanceTimer::setActive(false); PerformanceTimer::setActive(false);
@ -350,11 +349,6 @@ void OculusDisplayPlugin::deactivate() {
} }
void OculusDisplayPlugin::display(GLuint finalTexture, const glm::uvec2& sceneSize) { void OculusDisplayPlugin::display(GLuint finalTexture, const glm::uvec2& sceneSize) {
static bool inDisplay = false;
if (inDisplay) {
return;
}
inDisplay = true;
#if (OVR_MAJOR_VERSION >= 6) #if (OVR_MAJOR_VERSION >= 6)
using namespace oglplus; using namespace oglplus;
// Need to make sure only the display plugin is responsible for // Need to make sure only the display plugin is responsible for
@ -383,12 +377,14 @@ void OculusDisplayPlugin::display(GLuint finalTexture, const glm::uvec2& sceneSi
the UI visible in the output window (unlikely). This should be done before the UI visible in the output window (unlikely). This should be done before
_sceneFbo->Increment or we're be using the wrong texture _sceneFbo->Increment or we're be using the wrong texture
*/ */
if (_enableMirror) {
_sceneFbo->Bound(Framebuffer::Target::Read, [&] { _sceneFbo->Bound(Framebuffer::Target::Read, [&] {
glBlitFramebuffer( glBlitFramebuffer(
0, 0, _sceneFbo->size.x, _sceneFbo->size.y, 0, 0, _sceneFbo->size.x, _sceneFbo->size.y,
0, 0, windowSize.x, windowSize.y, 0, 0, windowSize.x, windowSize.y,
GL_COLOR_BUFFER_BIT, GL_NEAREST); GL_COLOR_BUFFER_BIT, GL_NEAREST);
}); });
}
{ {
PerformanceTimer("OculusSubmit"); PerformanceTimer("OculusSubmit");
@ -409,6 +405,7 @@ void OculusDisplayPlugin::display(GLuint finalTexture, const glm::uvec2& sceneSi
The other alternative for mirroring is to use the Oculus mirror texture support, which The other alternative for mirroring is to use the Oculus mirror texture support, which
will contain the post-distorted and fully composited scene regardless of how many layers will contain the post-distorted and fully composited scene regardless of how many layers
we send. we send.
Currently generates an error.
*/ */
//auto mirrorSize = _mirrorFbo->size; //auto mirrorSize = _mirrorFbo->size;
//_mirrorFbo->Bound(Framebuffer::Target::Read, [&] { //_mirrorFbo->Bound(Framebuffer::Target::Read, [&] {
@ -420,21 +417,10 @@ void OculusDisplayPlugin::display(GLuint finalTexture, const glm::uvec2& sceneSi
++_frameIndex; ++_frameIndex;
#endif #endif
inDisplay = false;
} }
// Pass input events on to the application // Pass input events on to the application
bool OculusDisplayPlugin::eventFilter(QObject* receiver, QEvent* event) { bool OculusDisplayPlugin::eventFilter(QObject* receiver, QEvent* event) {
#if (OVR_MAJOR_VERSION >= 6)
if (event->type() == QEvent::Resize) {
QResizeEvent* resizeEvent = static_cast<QResizeEvent*>(event);
qDebug() << resizeEvent->size().width() << " x " << resizeEvent->size().height();
auto newSize = toGlm(resizeEvent->size());
makeCurrent();
_mirrorFbo->Resize(newSize);
doneCurrent();
}
#endif
return WindowOpenGLDisplayPlugin::eventFilter(receiver, event); return WindowOpenGLDisplayPlugin::eventFilter(receiver, event);
} }
@ -444,7 +430,9 @@ bool OculusDisplayPlugin::eventFilter(QObject* receiver, QEvent* event) {
otherwise the swapbuffer delay will interefere with the framerate of the headset otherwise the swapbuffer delay will interefere with the framerate of the headset
*/ */
void OculusDisplayPlugin::finishFrame() { void OculusDisplayPlugin::finishFrame() {
//swapBuffers(); if (_enableMirror) {
swapBuffers();
}
doneCurrent(); doneCurrent();
}; };

View file

@ -58,6 +58,7 @@ private:
mat4 _compositeEyeProjections[2]; mat4 _compositeEyeProjections[2];
uvec2 _desiredFramebufferSize; uvec2 _desiredFramebufferSize;
ovrTrackingState _trackingState; ovrTrackingState _trackingState;
bool _enableMirror{ false };
#if (OVR_MAJOR_VERSION >= 6) #if (OVR_MAJOR_VERSION >= 6)
ovrHmd _hmd; ovrHmd _hmd;
@ -70,7 +71,6 @@ private:
ovrLayerEyeFov& getSceneLayer(); ovrLayerEyeFov& getSceneLayer();
ovrHmdDesc _hmdDesc; ovrHmdDesc _hmdDesc;
SwapFboPtr _sceneFbo; SwapFboPtr _sceneFbo;
MirrorFboPtr _mirrorFbo;
ovrLayerEyeFov _sceneLayer; ovrLayerEyeFov _sceneLayer;
#endif #endif
#if (OVR_MAJOR_VERSION == 7) #if (OVR_MAJOR_VERSION == 7)

View file

@ -0,0 +1,27 @@
//
// Created by Bradley Austin Davis on 2015/09/01
// Copyright 2013-2105 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
// Simulates a java finally block by executing a lambda when an instance leaves
// scope
#include <functional>
#pragma once
#ifndef hifi_Finally_h
#define hifi_Finally_h
class Finally {
public:
template <typename F>
Finally(F f) : _f(f) {}
~Finally() { _f(); }
private:
std::function<void()> _f;
};
#endif