Reduce crashes on switching display plugins

This commit is contained in:
Bradley Austin Davis 2016-03-16 16:58:59 -07:00 committed by Brad Davis
parent 9d15c190f7
commit 7045680bc7
35 changed files with 305 additions and 316 deletions

View file

@ -698,6 +698,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
ResourceCache::setRequestLimit(3);
_glWidget = new GLCanvas();
getApplicationCompositor().setRenderingWidget(_glWidget);
_window->setCentralWidget(_glWidget);
_window->restoreGeometry();
@ -4684,10 +4685,12 @@ qreal Application::getDevicePixelRatio() {
}
DisplayPlugin* Application::getActiveDisplayPlugin() {
if (nullptr == _displayPlugin) {
std::unique_lock<std::recursive_mutex> lock(_displayPluginLock);
if (nullptr == _displayPlugin && QThread::currentThread() == thread()) {
updateDisplayMode();
Q_ASSERT(_displayPlugin);
}
return _displayPlugin.get();
}
@ -4733,6 +4736,19 @@ static void addDisplayPluginToMenu(DisplayPluginPointer displayPlugin, bool acti
}
void Application::updateDisplayMode() {
// Unsafe to call this method from anything but the main thread
if (QThread::currentThread() != thread()) {
qFatal("Attempted to switch display plugins from a non-main thread");
}
// 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
// The current oculus runtime doesn't do this anymore
if (_inPaint) {
qFatal("Attempted to switch display plugins while in painting");
}
auto menu = Menu::getInstance();
auto displayPlugins = PluginManager::getInstance()->getDisplayPlugins();
@ -4789,70 +4805,25 @@ void Application::updateDisplayMode() {
}
}
if (newDisplayPlugin == _displayPlugin) {
return;
}
if (_displayPlugin) {
_displayPlugin->deactivate();
}
auto offscreenUi = DependencyManager::get<OffscreenUi>();
DisplayPluginPointer oldDisplayPlugin = _displayPlugin;
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();
QTimer* timer = new QTimer();
timer->singleShot(500, [this, timer] {
timer->deleteLater();
updateDisplayMode();
});
return;
}
if (!_pluginContainer->currentDisplayActions().isEmpty()) {
auto menu = Menu::getInstance();
foreach(auto itemInfo, _pluginContainer->currentDisplayActions()) {
menu->removeMenuItem(itemInfo.first, itemInfo.second);
}
_pluginContainer->currentDisplayActions().clear();
}
if (newDisplayPlugin) {
_offscreenContext->makeCurrent();
newDisplayPlugin->activate();
_offscreenContext->makeCurrent();
offscreenUi->resize(fromGlm(newDisplayPlugin->getRecommendedUiSize()));
_offscreenContext->makeCurrent();
}
oldDisplayPlugin = _displayPlugin;
// FIXME probably excessive and useless context switching
_offscreenContext->makeCurrent();
newDisplayPlugin->activate();
_offscreenContext->makeCurrent();
offscreenUi->resize(fromGlm(newDisplayPlugin->getRecommendedUiSize()));
_offscreenContext->makeCurrent();
getApplicationCompositor().setDisplayPlugin(newDisplayPlugin);
_displayPlugin = newDisplayPlugin;
getApplicationCompositor().setDisplayPlugin(_displayPlugin);
// If the displayPlugin is a screen based HMD, then it will want the HMDTools displayed
// Direct Mode HMDs (like windows Oculus) will be isHmd() but will have a screen of -1
bool newPluginWantsHMDTools = newDisplayPlugin ?
(newDisplayPlugin->isHmd() && (newDisplayPlugin->getHmdScreen() >= 0)) : false;
bool oldPluginWantedHMDTools = oldDisplayPlugin ?
(oldDisplayPlugin->isHmd() && (oldDisplayPlugin->getHmdScreen() >= 0)) : false;
// Only show the hmd tools after the correct plugin has
// been activated so that it's UI is setup correctly
if (newPluginWantsHMDTools) {
_pluginContainer->showDisplayPluginsTools();
}
if (oldDisplayPlugin) {
oldDisplayPlugin->deactivate();
_offscreenContext->makeCurrent();
// if the old plugin was HMD and the new plugin is not HMD, then hide our hmdtools
if (oldPluginWantedHMDTools && !newPluginWantsHMDTools) {
DependencyManager::get<DialogsManager>()->hmdTools(false);
}
}
emit activeDisplayPluginChanged();
// reset the avatar, to set head and hand palms back to a resonable default pose.

View file

@ -385,6 +385,7 @@ private:
OffscreenGLCanvas* _offscreenContext { nullptr };
DisplayPluginPointer _displayPlugin;
std::recursive_mutex _displayPluginLock;
InputPluginList _activeInputPlugins;
bool _activatingDisplayPlugin { false };

View file

@ -38,8 +38,8 @@ void PluginContainerProxy::requestReset() {
qApp->resetSensors(true);
}
void PluginContainerProxy::showDisplayPluginsTools() {
DependencyManager::get<DialogsManager>()->hmdTools(true);
void PluginContainerProxy::showDisplayPluginsTools(bool show) {
DependencyManager::get<DialogsManager>()->hmdTools(show);
}
GLWidget* PluginContainerProxy::getPrimaryWidget() {

View file

@ -14,7 +14,7 @@ class PluginContainerProxy : public QObject, PluginContainer {
Q_OBJECT
PluginContainerProxy();
virtual ~PluginContainerProxy();
virtual void showDisplayPluginsTools() override;
virtual void showDisplayPluginsTools(bool show = true) override;
virtual void requestReset() override;
virtual bool makeRenderingContextCurrent() override;
virtual void releaseSceneTexture(const gpu::TexturePointer& texture) override;

View file

@ -143,7 +143,9 @@ void DialogsManager::hmdTools(bool showTools) {
}
void DialogsManager::hmdToolsClosed() {
_hmdToolsDialog->hide();
if (_hmdToolsDialog) {
_hmdToolsDialog->hide();
}
}
void DialogsManager::showScriptEditor() {

View file

@ -19,8 +19,8 @@ const QString Basic2DWindowOpenGLDisplayPlugin::NAME("Desktop");
static const QString FULLSCREEN = "Fullscreen";
void Basic2DWindowOpenGLDisplayPlugin::activate() {
WindowOpenGLDisplayPlugin::activate();
void Basic2DWindowOpenGLDisplayPlugin::internalActivate() {
Parent::internalActivate();
_framerateActions.clear();
_container->addMenuItem(PluginType::DISPLAY_PLUGIN, MENU_PATH(), FULLSCREEN,
@ -37,14 +37,14 @@ void Basic2DWindowOpenGLDisplayPlugin::activate() {
void Basic2DWindowOpenGLDisplayPlugin::submitSceneTexture(uint32_t frameIndex, const gpu::TexturePointer& sceneTexture) {
_wantVsync = true; // always
WindowOpenGLDisplayPlugin::submitSceneTexture(frameIndex, sceneTexture);
Parent::submitSceneTexture(frameIndex, sceneTexture);
}
void Basic2DWindowOpenGLDisplayPlugin::internalPresent() {
if (_wantVsync != isVsyncEnabled()) {
enableVsync(_wantVsync);
}
WindowOpenGLDisplayPlugin::internalPresent();
Parent::internalPresent();
}
static const uint32_t MIN_THROTTLE_CHECK_FRAMES = 60;

View file

@ -7,22 +7,22 @@
//
#pragma once
#include "WindowOpenGLDisplayPlugin.h"
#include "OpenGLDisplayPlugin.h"
const float TARGET_FRAMERATE_Basic2DWindowOpenGL = 60.0f;
class QScreen;
class QAction;
class Basic2DWindowOpenGLDisplayPlugin : public WindowOpenGLDisplayPlugin {
class Basic2DWindowOpenGLDisplayPlugin : public OpenGLDisplayPlugin {
Q_OBJECT
using Parent = OpenGLDisplayPlugin;
public:
virtual const QString& getName() const override { return NAME; }
virtual float getTargetFrameRate() override { return _framerateTarget ? (float) _framerateTarget : TARGET_FRAMERATE_Basic2DWindowOpenGL; }
virtual void activate() override;
virtual void internalActivate() override;
virtual void submitSceneTexture(uint32_t frameIndex, const gpu::TexturePointer& sceneTexture) override;

View file

@ -21,7 +21,6 @@
#include <NumericalConstants.h>
#include <DependencyManager.h>
#include <plugins/PluginManager.h>
#include <plugins/PluginContainer.h>
#include <CursorManager.h>
#include <gl/GLWidget.h>
@ -188,16 +187,15 @@ void CompositorHelper::handleLeaveEvent() {
if (shouldCaptureMouse()) {
//QWidget* mainWidget = (QWidget*)qApp->getWindow();
static auto renderingWidget = PluginContainer::getInstance().getPrimaryWidget();
static QWidget* mainWidget = nullptr;
if (mainWidget == nullptr) {
mainWidget = renderingWidget->parentWidget();
if (mainWidget == nullptr && _renderingWidget != nullptr) {
mainWidget = _renderingWidget->parentWidget();
}
QRect mainWidgetFrame;
{
mainWidgetFrame = renderingWidget->geometry();
mainWidgetFrame = _renderingWidget->geometry();
auto topLeft = mainWidgetFrame.topLeft();
auto topLeftScreen = renderingWidget->mapToGlobal(topLeft);
auto topLeftScreen = _renderingWidget->mapToGlobal(topLeft);
mainWidgetFrame.moveTopLeft(topLeftScreen);
}
QRect uncoveredRect = mainWidgetFrame;
@ -293,16 +291,17 @@ void CompositorHelper::sendFakeMouseEvent() {
return;
}
if (_renderingWidget) {
// in HMD mode we need to fake our mouse moves...
QPoint globalPos(_reticlePositionInHMD.x, _reticlePositionInHMD.y);
auto button = Qt::NoButton;
auto buttons = QApplication::mouseButtons();
auto modifiers = QApplication::keyboardModifiers();
static auto renderingWidget = PluginContainer::getInstance().getPrimaryWidget();
QMouseEvent event(QEvent::MouseMove, globalPos, button, buttons, modifiers);
_fakeMouseEvent = true;
qApp->sendEvent(renderingWidget, &event);
_fakeMouseEvent = false;
QPoint globalPos(_reticlePositionInHMD.x, _reticlePositionInHMD.y);
auto button = Qt::NoButton;
auto buttons = QApplication::mouseButtons();
auto modifiers = QApplication::keyboardModifiers();
QMouseEvent event(QEvent::MouseMove, globalPos, button, buttons, modifiers);
_fakeMouseEvent = true;
qApp->sendEvent(_renderingWidget, &event);
_fakeMouseEvent = false;
}
}
void CompositorHelper::setReticlePosition(const glm::vec2& position, bool sendFakeEvent) {
@ -496,9 +495,8 @@ glm::mat4 CompositorHelper::getReticleTransform(const glm::mat4& eyePose, const
result = glm::scale(pointerTransform, reticleScale);
} else {
static const float CURSOR_PIXEL_SIZE = 32.0f;
static auto renderingWidget = PluginContainer::getInstance().getPrimaryWidget();
const auto canvasSize = vec2(toGlm(renderingWidget->size()));;
vec2 mousePosition = toGlm(renderingWidget->mapFromGlobal(QCursor::pos()));
const auto canvasSize = vec2(toGlm(_renderingWidget->size()));;
vec2 mousePosition = toGlm(_renderingWidget->mapFromGlobal(QCursor::pos()));
mousePosition /= canvasSize;
mousePosition *= 2.0;
mousePosition -= 1.0;

View file

@ -48,6 +48,7 @@ public:
static const vec2 MOUSE_EXTENTS_PIXELS;
CompositorHelper();
void setRenderingWidget(QWidget* widget) { _renderingWidget = widget; }
bool calculateRayUICollisionPoint(const glm::vec3& position, const glm::vec3& direction, glm::vec3& result) const;
@ -126,6 +127,7 @@ private:
DisplayPluginPointer _currentDisplayPlugin;
glm::mat4 _currentCamera;
uint32_t _currentFrame { 0 };
QWidget* _renderingWidget{ nullptr };
//// Support for hovering and tooltips
//static EntityItemID _noItemId;

View file

@ -30,8 +30,6 @@ void NullDisplayPlugin::submitOverlayTexture(const gpu::TexturePointer& overlayT
_container->releaseOverlayTexture(overlayTexture);
}
void NullDisplayPlugin::stop() {}
QImage NullDisplayPlugin::getScreenshot() const {
return QImage();
}

View file

@ -16,8 +16,6 @@ public:
virtual const QString& getName() const override { return NAME; }
virtual grouping getGrouping() const override { return DEVELOPER; }
void stop() override;
virtual glm::uvec2 getRecommendedRenderSize() const override;
virtual bool hasFocus() const override;
virtual void submitSceneTexture(uint32_t frameIndex, const gpu::TexturePointer& sceneTexture) override;

View file

@ -54,6 +54,9 @@ public:
void shutdown() {
if (isRunning()) {
// First ensure that we turn off any current display plugin
setNewDisplayPlugin(nullptr);
Lock lock(_mutex);
_shutdown = true;
_condition.wait(lock, [&] { return !_shutdown; });
@ -64,7 +67,10 @@ public:
void setNewDisplayPlugin(OpenGLDisplayPlugin* plugin) {
Lock lock(_mutex);
_newPlugin = plugin;
if (isRunning()) {
_newPluginQueue.push(plugin);
_condition.wait(lock, [=]()->bool { return _newPluginQueue.empty(); });
}
}
void setContext(QGLContext * context) {
@ -98,37 +104,42 @@ public:
}
}
// Check before lock
if (_newPlugin != nullptr) {
// Check for a new display plugin
{
Lock lock(_mutex);
_context->makeCurrent();
// Check if we have a new plugin to activate
if (_newPlugin != nullptr) {
// Deactivate the old plugin
if (currentPlugin != nullptr) {
try {
currentPlugin->uncustomizeContext();
} catch (const oglplus::Error& error) {
qWarning() << "OpenGL error in uncustomizeContext: " << error.what();
while (!_newPluginQueue.empty()) {
auto newPlugin = _newPluginQueue.front();
if (newPlugin != currentPlugin) {
// Deactivate the old plugin
if (currentPlugin != nullptr) {
try {
currentPlugin->uncustomizeContext();
} catch (const oglplus::Error& error) {
qWarning() << "OpenGL error in uncustomizeContext: " << error.what();
}
}
currentPlugin->enableDeactivate();
}
try {
_newPlugin->customizeContext();
} catch (const oglplus::Error& error) {
qWarning() << "OpenGL error in customizeContext: " << error.what();
if (newPlugin) {
try {
newPlugin->customizeContext();
} catch (const oglplus::Error& error) {
qWarning() << "OpenGL error in customizeContext: " << error.what();
}
}
currentPlugin = newPlugin;
_newPluginQueue.pop();
_condition.notify_one();
}
currentPlugin = _newPlugin;
_newPlugin = nullptr;
}
_context->doneCurrent();
lock.unlock();
}
// If there's no active plugin, just sleep
if (currentPlugin == nullptr) {
QThread::usleep(100);
// Minimum sleep ends up being about 2 ms anyway
QThread::msleep(1);
continue;
}
@ -146,15 +157,8 @@ public:
}
}
_context->makeCurrent();
if (currentPlugin) {
currentPlugin->uncustomizeContext();
currentPlugin->enableDeactivate();
}
_context->doneCurrent();
_context->moveToThread(qApp->thread());
Lock lock(_mutex);
_context->moveToThread(qApp->thread());
_shutdown = false;
_condition.notify_one();
}
@ -192,7 +196,7 @@ private:
bool _pendingMainThreadOperation { false };
bool _finishedMainThreadOperation { false };
QThread* _mainThread { nullptr };
OpenGLDisplayPlugin* _newPlugin { nullptr };
std::queue<OpenGLDisplayPlugin*> _newPluginQueue;
QGLContext* _context { nullptr };
};
@ -231,6 +235,12 @@ void OpenGLDisplayPlugin::activate() {
}
_vsyncSupported = _container->getPrimaryWidget()->isVsyncSupported();
// Child classes may override this in order to do things like initialize
// libraries, etc
internalActivate();
#if THREADED_PRESENT
// Start the present thread if necessary
auto presentThread = DependencyManager::get<PresentThread>();
@ -243,6 +253,9 @@ void OpenGLDisplayPlugin::activate() {
// Start execution
presentThread->start();
}
// This should not return until the new context has been customized
// and the old context (if any) has been uncustomized
presentThread->setNewDisplayPlugin(this);
#else
static auto widget = _container->getPrimaryWidget();
@ -253,28 +266,25 @@ void OpenGLDisplayPlugin::activate() {
DisplayPlugin::activate();
}
void OpenGLDisplayPlugin::stop() {
}
void OpenGLDisplayPlugin::deactivate() {
#if THREADED_PRESENT
{
Lock lock(_mutex);
_deactivateWait.wait(lock, [&]{ return _uncustomized; });
}
auto presentThread = DependencyManager::get<PresentThread>();
// Does not return until the GL transition has completeed
presentThread->setNewDisplayPlugin(nullptr);
#else
static auto widget = _container->getPrimaryWidget();
widget->makeCurrent();
uncustomizeContext();
_container->makeRenderingContextCurrent();
#endif
internalDeactivate();
DisplayPlugin::deactivate();
}
void OpenGLDisplayPlugin::customizeContext() {
#if THREADED_PRESENT
_uncustomized = false;
auto presentThread = DependencyManager::get<PresentThread>();
Q_ASSERT(thread() == presentThread->thread());
#endif
@ -393,13 +403,12 @@ void OpenGLDisplayPlugin::submitOverlayTexture(const gpu::TexturePointer& overla
}
void OpenGLDisplayPlugin::updateTextures() {
auto oldSceneTexture = _currentSceneTexture;
_currentSceneTexture = _sceneTextureEscrow.fetchAndRelease(_currentSceneTexture);
if (oldSceneTexture != _currentSceneTexture) {
// FIXME intrduce a GPU wait instead of a CPU/GPU sync point?
if (_sceneTextureEscrow.fetchSignaledAndRelease(_currentSceneTexture)) {
updateFrameData();
}
_currentOverlayTexture = _overlayTextureEscrow.fetchAndRelease(_currentOverlayTexture);
_overlayTextureEscrow.fetchSignaledAndRelease(_currentOverlayTexture);
}
void OpenGLDisplayPlugin::updateFrameData() {
@ -407,7 +416,6 @@ void OpenGLDisplayPlugin::updateFrameData() {
_currentRenderFrameIndex = _sceneTextureToFrameIndexMap[_currentSceneTexture];
}
void OpenGLDisplayPlugin::updateFramerate() {
uint64_t now = usecTimestampNow();
static uint64_t lastSwapEnd { now };
@ -593,14 +601,6 @@ QImage OpenGLDisplayPlugin::getScreenshot() const {
return result;
}
#if THREADED_PRESENT
void OpenGLDisplayPlugin::enableDeactivate() {
Lock lock(_mutex);
_uncustomized = true;
_deactivateWait.notify_one();
}
#endif
uint32_t OpenGLDisplayPlugin::getSceneTextureId() const {
if (!_currentSceneTexture) {
return 0;
@ -625,3 +625,26 @@ void OpenGLDisplayPlugin::eyeViewport(Eye eye) const {
}
Context::Viewport(vpPos.x, vpPos.y, vpSize.x, vpSize.y);
}
glm::uvec2 OpenGLDisplayPlugin::getSurfacePixels() const {
uvec2 result;
auto window = _container->getPrimaryWidget();
if (window) {
result = toGlm(window->geometry().size() * window->devicePixelRatio());
}
return result;
}
glm::uvec2 OpenGLDisplayPlugin::getSurfaceSize() const {
uvec2 result;
auto window = _container->getPrimaryWidget();
if (window) {
result = toGlm(window->geometry().size());
}
return result;
}
bool OpenGLDisplayPlugin::hasFocus() const {
auto window = _container->getPrimaryWidget();
return window ? window->hasFocus() : false;
}

View file

@ -29,9 +29,12 @@ protected:
using TextureEscrow = GLEscrow<gpu::TexturePointer>;
public:
OpenGLDisplayPlugin();
void activate() override;
void deactivate() override;
void stop() override;
// These must be final to ensure proper ordering of operations
// between the main thread and the presentation thread
void activate() override final;
void deactivate() override final;
bool eventFilter(QObject* receiver, QEvent* event) override;
bool isDisplayVisible() const override { return true; }
@ -56,13 +59,15 @@ protected:
#endif
uint32_t getSceneTextureId() const;
uint32_t getOverlayTextureId() const;
glm::uvec2 getSurfaceSize() const;
glm::uvec2 getSurfacePixels() const;
void compositeLayers();
virtual void compositeOverlay();
virtual void compositePointer();
virtual glm::uvec2 getSurfaceSize() const = 0;
virtual glm::uvec2 getSurfacePixels() const = 0;
virtual bool hasFocus() const override;
// FIXME make thread safe?
virtual bool isVsyncEnabled();
@ -71,6 +76,9 @@ protected:
// These functions must only be called on the presentation thread
virtual void customizeContext();
virtual void uncustomizeContext();
virtual void internalActivate() {}
virtual void internalDeactivate() {}
virtual void cleanupForSceneTexture(const gpu::TexturePointer& sceneTexture);
// Plugin specific functionality to send the composed scene to the output window or device
virtual void internalPresent();
@ -113,14 +121,6 @@ protected:
std::map<uint16_t, CursorData> _cursorsData;
BasicFramebufferWrapperPtr _compositeFramebuffer;
private:
#if THREADED_PRESENT
void enableDeactivate();
Condition _deactivateWait;
bool _uncustomized{ false };
#endif
};

View file

@ -1,43 +0,0 @@
//
// Created by Bradley Austin Davis on 2015/05/29
// Copyright 2015 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
//
#include "WindowOpenGLDisplayPlugin.h"
#include <gl/GLWidget.h>
#include "plugins/PluginContainer.h"
glm::uvec2 WindowOpenGLDisplayPlugin::getSurfacePixels() const {
uvec2 result;
if (_window) {
result = toGlm(_window->geometry().size() * _window->devicePixelRatio());
}
return result;
}
glm::uvec2 WindowOpenGLDisplayPlugin::getSurfaceSize() const {
uvec2 result;
if (_window) {
result = toGlm(_window->geometry().size());
}
return result;
}
bool WindowOpenGLDisplayPlugin::hasFocus() const {
return _window ? _window->hasFocus() : false;
}
void WindowOpenGLDisplayPlugin::activate() {
_window = _container->getPrimaryWidget();
OpenGLDisplayPlugin::activate();
}
void WindowOpenGLDisplayPlugin::deactivate() {
OpenGLDisplayPlugin::deactivate();
_window = nullptr;
}

View file

@ -1,25 +0,0 @@
//
// Created by Bradley Austin Davis on 2015/05/29
// Copyright 2015 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
//
#pragma once
#include "OpenGLDisplayPlugin.h"
class QWidget;
class WindowOpenGLDisplayPlugin : public OpenGLDisplayPlugin {
public:
virtual bool hasFocus() const override;
virtual void activate() override;
virtual void deactivate() override;
protected:
virtual glm::uvec2 getSurfaceSize() const override final;
virtual glm::uvec2 getSurfacePixels() const override final;
QWidget* _window { nullptr };
};

View file

@ -18,6 +18,7 @@
#include <plugins/PluginContainer.h>
#include <gpu/GLBackend.h>
#include <CursorManager.h>
#include <gl/GLWidget.h>
#include "../Logging.h"
#include "../CompositorHelper.h"
@ -30,7 +31,7 @@ glm::uvec2 HmdDisplayPlugin::getRecommendedUiSize() const {
return CompositorHelper::VIRTUAL_SCREEN_SIZE;
}
void HmdDisplayPlugin::activate() {
void HmdDisplayPlugin::internalActivate() {
_monoPreview = _container->getBoolSetting("monoPreview", DEFAULT_MONO_VIEW);
_container->addMenuItem(PluginType::DISPLAY_PLUGIN, MENU_PATH(), MONO_PREVIEW,
@ -39,11 +40,7 @@ void HmdDisplayPlugin::activate() {
_container->setBoolSetting("monoPreview", _monoPreview);
}, true, _monoPreview);
_container->removeMenu(FRAMERATE);
Parent::activate();
}
void HmdDisplayPlugin::deactivate() {
Parent::deactivate();
Parent::internalActivate();
}
void HmdDisplayPlugin::customizeContext() {
@ -114,7 +111,8 @@ void HmdDisplayPlugin::internalPresent() {
// screen preview mirroring
if (_enablePreview) {
auto windowSize = toGlm(_window->size());
auto window = _container->getPrimaryWidget();
auto windowSize = toGlm(window->size());
float windowAspect = aspect(windowSize);
float sceneAspect = aspect(_renderTargetSize);
if (_monoPreview) {

View file

@ -9,10 +9,10 @@
#include <QtGlobal>
#include "../WindowOpenGLDisplayPlugin.h"
#include "../OpenGLDisplayPlugin.h"
class HmdDisplayPlugin : public WindowOpenGLDisplayPlugin {
using Parent = WindowOpenGLDisplayPlugin;
class HmdDisplayPlugin : public OpenGLDisplayPlugin {
using Parent = OpenGLDisplayPlugin;
public:
bool isHmd() const override final { return true; }
float getIPD() const override final { return _ipd; }
@ -25,13 +25,12 @@ public:
bool isDisplayVisible() const override { return isHmdMounted(); }
void activate() override;
void deactivate() override;
protected:
virtual void hmdPresent() = 0;
virtual bool isHmdMounted() const = 0;
void internalActivate() override;
void compositeOverlay() override;
void compositePointer() override;
void internalPresent() override;

View file

@ -58,7 +58,7 @@ void InterleavedStereoDisplayPlugin::uncustomizeContext() {
}
glm::uvec2 InterleavedStereoDisplayPlugin::getRecommendedRenderSize() const {
uvec2 result = WindowOpenGLDisplayPlugin::getRecommendedRenderSize();
uvec2 result = Parent::getRecommendedRenderSize();
result.x *= 2;
result.y /= 2;
return result;

View file

@ -11,6 +11,7 @@
class InterleavedStereoDisplayPlugin : public StereoDisplayPlugin {
Q_OBJECT
using Parent = StereoDisplayPlugin;
public:
const QString& getName() const override { return NAME; }
grouping getGrouping() const override { return ADVANCED; }

View file

@ -15,11 +15,8 @@
const QString SideBySideStereoDisplayPlugin::NAME("3D TV - Side by Side Stereo");
SideBySideStereoDisplayPlugin::SideBySideStereoDisplayPlugin() {
}
glm::uvec2 SideBySideStereoDisplayPlugin::getRecommendedRenderSize() const {
uvec2 result = WindowOpenGLDisplayPlugin::getRecommendedRenderSize();
uvec2 result = Parent::getRecommendedRenderSize();
result.x *= 2;
return result;
}

View file

@ -13,8 +13,8 @@ class QScreen;
class SideBySideStereoDisplayPlugin : public StereoDisplayPlugin {
Q_OBJECT
using Parent = StereoDisplayPlugin;
public:
SideBySideStereoDisplayPlugin();
virtual const QString& getName() const override { return NAME; }
virtual grouping getGrouping() const override { return ADVANCED; }
virtual glm::uvec2 getRecommendedRenderSize() const override;

View file

@ -58,7 +58,7 @@ glm::mat4 StereoDisplayPlugin::getEyeProjection(Eye eye, const glm::mat4& basePr
static const QString FRAMERATE = DisplayPlugin::MENU_PATH() + ">Framerate";
std::vector<QAction*> _screenActions;
void StereoDisplayPlugin::activate() {
void StereoDisplayPlugin::internalActivate() {
auto screens = qApp->screens();
_screenActions.resize(screens.size());
for (int i = 0; i < screens.size(); ++i) {
@ -77,7 +77,7 @@ void StereoDisplayPlugin::activate() {
_screen = qApp->primaryScreen();
_container->setFullscreen(_screen);
WindowOpenGLDisplayPlugin::activate();
Parent::internalActivate();
}
void StereoDisplayPlugin::updateScreen() {
@ -90,15 +90,15 @@ void StereoDisplayPlugin::updateScreen() {
}
}
void StereoDisplayPlugin::deactivate() {
void StereoDisplayPlugin::internalDeactivate() {
_screenActions.clear();
_container->unsetFullscreen();
WindowOpenGLDisplayPlugin::deactivate();
Parent::internalDeactivate();
}
// Derived classes will override the recommended render size based on the window size,
// so here we want to fix the aspect ratio based on the window, not on the render size
float StereoDisplayPlugin::getRecommendedAspectRatio() const {
return aspect(WindowOpenGLDisplayPlugin::getRecommendedRenderSize());
return aspect(Parent::getRecommendedRenderSize());
}

View file

@ -7,19 +7,16 @@
//
#pragma once
#include "../WindowOpenGLDisplayPlugin.h"
#include "../OpenGLDisplayPlugin.h"
class QScreen;
class StereoDisplayPlugin : public WindowOpenGLDisplayPlugin {
class StereoDisplayPlugin : public OpenGLDisplayPlugin {
Q_OBJECT
using Parent = WindowOpenGLDisplayPlugin;
using Parent = OpenGLDisplayPlugin;
public:
virtual bool isStereo() const override final { return true; }
virtual bool isSupported() const override final;
virtual void activate() override;
virtual void deactivate() override;
virtual float getRecommendedAspectRatio() const override;
virtual glm::mat4 getEyeProjection(Eye eye, const glm::mat4& baseProjection) const override;
@ -32,7 +29,10 @@ public:
// virtual glm::mat4 getEyeToHeadTransform(Eye eye) const override;
protected:
virtual void internalActivate() override;
virtual void internalDeactivate() override;
void updateScreen();
float _ipd{ 0.064f };
QScreen* _screen;
};

View file

@ -138,34 +138,100 @@ public:
// Returns the next available resource provided by the submitter,
// or if none is available (which could mean either the submission
// list is empty or that the first item on the list isn't yet signaled
T fetch() {
T result = invalid();
// Deprecated... will inject an unecessary GPU bubble
bool fetchSignaled(T& t) {
bool result = false;
// On the one hand using try_lock() reduces the chance of blocking the consumer thread,
// but if the produce thread is going fast enough, it could effectively
// starve the consumer out of ever actually getting resources.
tryLock([&]{
tryLock([&] {
// May be called on any thread, but must be inside a locked section
if (signaled(_submits, 0)) {
result = _submits.at(0)._value;
result = true;
t = _submits.at(0)._value;
_submits.pop_front();
}
});
return result;
}
// Populates t with the next available resource provided by the submitter
// and sync with a fence that will be signaled when all write commands for the
// item have completed. Returns false if no resources are available
bool fetchWithFence(T& t, GLsync& sync) {
bool result = false;
// On the one hand using try_lock() reduces the chance of blocking the consumer thread,
// but if the produce thread is going fast enough, it could effectively
// starve the consumer out of ever actually getting resources.
tryLock([&] {
if (!_submits.empty()) {
result = true;
// When fetching with sync, we only want the latest item
auto item = _submits.back();
_submits.pop_back();
// Throw everything else in the trash
for (const auto& oldItem : _submits) {
_trash.push_front(oldItem);
}
_submits.clear();
t = item._value;
sync = item._sync;
}
});
return result;
}
bool fetchWithGpuWait(T& t) {
GLsync sync { 0 };
if (fetchWithFence(t, sync)) {
// Texture was updated, inject a wait into the GL command stream to ensure
// commands on this context until the commands to generate t are finished.
if (sync != 0) {
glWaitSync(sync, 0, GL_TIMEOUT_IGNORED);
glDeleteSync(sync);
}
return true;
}
return false;
}
// Returns the next available resource provided by the submitter,
// or if none is available (which could mean either the submission
// list is empty or that the first item on the list isn't yet signaled
// Also releases any previous texture held by the caller
T fetchAndRelease(const T& oldValue) {
T result = fetch();
if (!result) {
return oldValue;
bool fetchSignaledAndRelease(T& value) {
T originalValue = value;
if (fetchSignaled(value)) {
if (originalValue != invalid()) {
release(originalValue);
}
return true;
}
if (oldValue) {
release(oldValue);
return false;
}
bool fetchAndReleaseWithFence(T& value, GLsync& sync) {
T originalValue = value;
if (fetchWithFence(value, sync)) {
if (originalValue != invalid()) {
release(originalValue);
}
return true;
}
return result;
return false;
}
bool fetchAndReleaseWithGpuWait(T& value) {
T originalValue = value;
if (fetchWithGpuWait(value)) {
if (originalValue != invalid()) {
release(originalValue);
}
return true;
}
return false;
}
// If fetch returns a non-zero value, it's the responsibility of the

View file

@ -498,12 +498,7 @@ void OffscreenQmlSurface::updateQuick() {
_render = false;
}
GLuint newTexture = _renderer->_escrow.fetch();
if (newTexture) {
if (_currentTexture) {
_renderer->_escrow.release(_currentTexture);
}
_currentTexture = newTexture;
if (_renderer->_escrow.fetchSignaledAndRelease(_currentTexture)) {
emit textureUpdated(_currentTexture);
}
}

View file

@ -0,0 +1,26 @@
#include "DisplayPlugin.h"
#include <ui/Menu.h>
#include "PluginContainer.h"
void DisplayPlugin::activate() {
Parent::activate();
if (isHmd() && (getHmdScreen() >= 0)) {
_container->showDisplayPluginsTools();
}
}
void DisplayPlugin::deactivate() {
_container->showDisplayPluginsTools(false);
if (!_container->currentDisplayActions().isEmpty()) {
auto menu = _container->getPrimaryMenu();
foreach(auto itemInfo, _container->currentDisplayActions()) {
menu->removeMenuItem(itemInfo.first, itemInfo.second);
}
_container->currentDisplayActions().clear();
}
Parent::deactivate();
}

View file

@ -57,7 +57,10 @@ namespace gpu {
class DisplayPlugin : public Plugin {
Q_OBJECT
using Parent = Plugin;
public:
void activate() override;
void deactivate() override;
virtual bool isHmd() const { return false; }
virtual int getHmdScreen() const { return -1; }
/// By default, all HMDs are stereo
@ -73,11 +76,6 @@ public:
// 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;
/**
* Sends the scene texture to the display plugin.
*/

View file

@ -55,7 +55,7 @@ public:
void unsetFullscreen(const QScreen* avoidScreen = nullptr);
virtual ui::Menu* getPrimaryMenu() = 0;
virtual void showDisplayPluginsTools() = 0;
virtual void showDisplayPluginsTools(bool show = true) = 0;
virtual void requestReset() = 0;
virtual bool makeRenderingContextCurrent() = 0;
virtual void releaseSceneTexture(const gpu::TexturePointer& texture) = 0;

View file

@ -34,16 +34,10 @@ void OculusBaseDisplayPlugin::customizeContext() {
glewExperimental = true;
GLenum err = glewInit();
glGetError();
HmdDisplayPlugin::customizeContext();
Parent::customizeContext();
}
void OculusBaseDisplayPlugin::init() {
}
void OculusBaseDisplayPlugin::deinit() {
}
void OculusBaseDisplayPlugin::activate() {
void OculusBaseDisplayPlugin::internalActivate() {
_session = acquireOculusSession();
_hmdDesc = ovr_GetHmdDesc(_session);
@ -90,11 +84,11 @@ void OculusBaseDisplayPlugin::activate() {
// This must come after the initialization, so that the values calculated
// above are available during the customizeContext call (when not running
// in threaded present mode)
HmdDisplayPlugin::activate();
Parent::internalActivate();
}
void OculusBaseDisplayPlugin::deactivate() {
HmdDisplayPlugin::deactivate();
void OculusBaseDisplayPlugin::internalDeactivate() {
Parent::internalDeactivate();
releaseOculusSession();
_session = nullptr;
}

View file

@ -14,21 +14,18 @@
#include <OVR_CAPI_GL.h>
class OculusBaseDisplayPlugin : public HmdDisplayPlugin {
using Parent = HmdDisplayPlugin;
public:
virtual bool isSupported() const override;
virtual void init() override final;
virtual void deinit() override final;
virtual void activate() override;
virtual void deactivate() override;
// Stereo specific methods
virtual void resetSensors() override final;
virtual glm::mat4 getHeadPose(uint32_t frameIndex) const override;
protected:
virtual void customizeContext() override;
void customizeContext() override;
void internalActivate() override;
void internalDeactivate() override;
protected:
ovrSession _session;

View file

@ -10,12 +10,8 @@
const QString OculusDisplayPlugin::NAME("Oculus Rift");
void OculusDisplayPlugin::activate() {
OculusBaseDisplayPlugin::activate();
}
void OculusDisplayPlugin::customizeContext() {
OculusBaseDisplayPlugin::customizeContext();
Parent::customizeContext();
_sceneFbo = SwapFboPtr(new SwapFramebufferWrapper(_session));
_sceneFbo->Init(getRecommendedRenderSize());
@ -34,7 +30,7 @@ void OculusDisplayPlugin::uncustomizeContext() {
#if (OVR_MAJOR_VERSION >= 6)
_sceneFbo.reset();
#endif
OculusBaseDisplayPlugin::uncustomizeContext();
Parent::uncustomizeContext();
}

View file

@ -17,7 +17,6 @@ const float TARGET_RATE_Oculus = 75.0f;
class OculusDisplayPlugin : public OculusBaseDisplayPlugin {
using Parent = OculusBaseDisplayPlugin;
public:
void activate() override;
const QString& getName() const override { return NAME; }
float getTargetFrameRate() override { return TARGET_RATE_Oculus; }

View file

@ -41,7 +41,8 @@ bool OpenVrDisplayPlugin::isSupported() const {
return vr::VR_IsHmdPresent();
}
void OpenVrDisplayPlugin::activate() {
void OpenVrDisplayPlugin::internalActivate() {
Parent::internalActivate();
_container->setIsOptionChecked(StandingHMDSensorMode, true);
if (!_system) {
@ -67,7 +68,6 @@ void OpenVrDisplayPlugin::activate() {
_compositor = vr::VRCompositor();
Q_ASSERT(_compositor);
HmdDisplayPlugin::activate();
// set up default sensor space such that the UI overlay will align with the front of the room.
auto chaperone = vr::VRChaperone();
@ -85,11 +85,8 @@ void OpenVrDisplayPlugin::activate() {
}
}
void OpenVrDisplayPlugin::deactivate() {
// Base class deactivate must come before our local deactivate
// because the OpenGL base class handles the wait for the present
// thread before continuing
HmdDisplayPlugin::deactivate();
void OpenVrDisplayPlugin::internalDeactivate() {
Parent::internalDeactivate();
_container->setIsOptionChecked(StandingHMDSensorMode, false);
if (_system) {
releaseOpenVrSystem();
@ -106,7 +103,7 @@ void OpenVrDisplayPlugin::customizeContext() {
GLenum err = glewInit();
glGetError();
});
HmdDisplayPlugin::customizeContext();
Parent::customizeContext();
}
void OpenVrDisplayPlugin::resetSensors() {

View file

@ -16,15 +16,13 @@
const float TARGET_RATE_OpenVr = 90.0f; // FIXME: get from sdk tracked device property? This number is vive-only.
class OpenVrDisplayPlugin : public HmdDisplayPlugin {
using Parent = HmdDisplayPlugin;
public:
virtual bool isSupported() const override;
virtual const QString& getName() const override { return NAME; }
virtual float getTargetFrameRate() override { return TARGET_RATE_OpenVr; }
virtual void activate() override;
virtual void deactivate() override;
virtual void customizeContext() override;
// Stereo specific methods
@ -32,6 +30,9 @@ public:
virtual glm::mat4 getHeadPose(uint32_t frameIndex) const override;
protected:
void internalActivate() override;
void internalDeactivate() override;
void hmdPresent() override;
bool isHmdMounted() const override;

View file

@ -81,7 +81,7 @@ class PluginContainerProxy : public QObject, PluginContainer {
Q_OBJECT
public:
virtual ~PluginContainerProxy() {}
virtual void showDisplayPluginsTools() override {}
virtual void showDisplayPluginsTools(bool show) override {}
virtual void requestReset() override {}
virtual bool makeRenderingContextCurrent() override { return true; }
virtual void releaseSceneTexture(const gpu::TexturePointer& texture) override {}