Merge pull request #6548 from jherico/threaded_present

Threaded present
This commit is contained in:
Brad Hefta-Gaub 2015-12-04 13:42:09 -08:00
commit f71d2d00d2
44 changed files with 677 additions and 363 deletions

View file

@ -45,7 +45,12 @@ Item {
Text { Text {
color: root.fontColor; color: root.fontColor;
font.pixelSize: root.fontSize font.pixelSize: root.fontSize
text: "Framerate: " + root.framerate text: "Render Rate: " + root.renderrate
}
Text {
color: root.fontColor;
font.pixelSize: root.fontSize
text: "Present Rate: " + root.presentrate
} }
Text { Text {
color: root.fontColor; color: root.fontColor;

View file

@ -44,6 +44,9 @@
#include <QtNetwork/QNetworkDiskCache> #include <QtNetwork/QNetworkDiskCache>
#include <gl/Config.h>
#include <QtGui/QOpenGLContext>
#include <AccountManager.h> #include <AccountManager.h>
#include <AddressManager.h> #include <AddressManager.h>
#include <ApplicationVersion.h> #include <ApplicationVersion.h>
@ -617,6 +620,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
// enable mouse tracking; otherwise, we only get drag events // enable mouse tracking; otherwise, we only get drag events
_glWidget->setMouseTracking(true); _glWidget->setMouseTracking(true);
_glWidget->makeCurrent();
_glWidget->initializeGL();
_offscreenContext = new OffscreenGLCanvas(); _offscreenContext = new OffscreenGLCanvas();
_offscreenContext->create(_glWidget->context()->contextHandle()); _offscreenContext->create(_glWidget->context()->contextHandle());
@ -1136,7 +1141,7 @@ void Application::paintGL() {
_lastInstantaneousFps = instantaneousFps; _lastInstantaneousFps = instantaneousFps;
auto displayPlugin = getActiveDisplayPlugin(); auto displayPlugin = getActiveDisplayPlugin();
displayPlugin->preRender(); // FIXME not needed anymore?
_offscreenContext->makeCurrent(); _offscreenContext->makeCurrent();
// update the avatar with a fresh HMD pose // update the avatar with a fresh HMD pose
@ -1191,6 +1196,9 @@ void Application::paintGL() {
QSize size = getDeviceSize(); QSize size = getDeviceSize();
renderArgs._viewport = glm::ivec4(0, 0, size.width(), size.height()); renderArgs._viewport = glm::ivec4(0, 0, size.width(), size.height());
_applicationOverlay.renderOverlay(&renderArgs); _applicationOverlay.renderOverlay(&renderArgs);
gpu::FramebufferPointer overlayFramebuffer = _applicationOverlay.getOverlayFramebuffer();
} }
{ {
@ -1304,6 +1312,13 @@ void Application::paintGL() {
auto baseProjection = renderArgs._viewFrustum->getProjection(); auto baseProjection = renderArgs._viewFrustum->getProjection();
auto hmdInterface = DependencyManager::get<HMDScriptingInterface>(); auto hmdInterface = DependencyManager::get<HMDScriptingInterface>();
float IPDScale = hmdInterface->getIPDScale(); float IPDScale = hmdInterface->getIPDScale();
// Tell the plugin what pose we're using to render. In this case we're just using the
// unmodified head pose because the only plugin that cares (the Oculus plugin) uses it
// for rotational timewarp. If we move to support positonal timewarp, we need to
// ensure this contains the full pose composed with the eye offsets.
mat4 headPose = displayPlugin->getHeadPose(_frameCount);
// FIXME we probably don't need to set the projection matrix every frame, // FIXME we probably don't need to set the projection matrix every frame,
// only when the display plugin changes (or in non-HMD modes when the user // only when the display plugin changes (or in non-HMD modes when the user
// changes the FOV manually, which right now I don't think they can. // changes the FOV manually, which right now I don't think they can.
@ -1319,12 +1334,7 @@ void Application::paintGL() {
mat4 eyeOffsetTransform = glm::translate(mat4(), eyeOffset * -1.0f * IPDScale); mat4 eyeOffsetTransform = glm::translate(mat4(), eyeOffset * -1.0f * IPDScale);
eyeOffsets[eye] = eyeOffsetTransform; eyeOffsets[eye] = eyeOffsetTransform;
// Tell the plugin what pose we're using to render. In this case we're just using the displayPlugin->setEyeRenderPose(_frameCount, eye, headPose);
// unmodified head pose because the only plugin that cares (the Oculus plugin) uses it
// for rotational timewarp. If we move to support positonal timewarp, we need to
// ensure this contains the full pose composed with the eye offsets.
mat4 headPose = displayPlugin->getHeadPose();
displayPlugin->setEyeRenderPose(eye, headPose);
eyeProjections[eye] = displayPlugin->getProjection(eye, baseProjection); eyeProjections[eye] = displayPlugin->getProjection(eye, baseProjection);
}); });
@ -1339,6 +1349,7 @@ void Application::paintGL() {
} }
// Overlay Composition, needs to occur after screen space effects have completed // Overlay Composition, needs to occur after screen space effects have completed
// FIXME migrate composition into the display plugins
{ {
PROFILE_RANGE(__FUNCTION__ "/compositor"); PROFILE_RANGE(__FUNCTION__ "/compositor");
PerformanceTimer perfTimer("compositor"); PerformanceTimer perfTimer("compositor");
@ -1367,44 +1378,40 @@ void Application::paintGL() {
{ {
PROFILE_RANGE(__FUNCTION__ "/pluginOutput"); PROFILE_RANGE(__FUNCTION__ "/pluginOutput");
PerformanceTimer perfTimer("pluginOutput"); PerformanceTimer perfTimer("pluginOutput");
auto primaryFbo = framebufferCache->getPrimaryFramebuffer(); auto primaryFramebuffer = framebufferCache->getPrimaryFramebuffer();
GLuint finalTexture = gpu::GLBackend::getTextureID(primaryFbo->getRenderBuffer(0)); auto scratchFramebuffer = framebufferCache->getFramebuffer();
// Ensure the rendering context commands are completed when rendering gpu::doInBatch(renderArgs._context, [=](gpu::Batch& batch) {
GLsync sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); gpu::Vec4i rect;
// Ensure the sync object is flushed to the driver thread before releasing the context rect.z = size.width();
// CRITICAL for the mac driver apparently. rect.w = size.height();
glFlush(); batch.setFramebuffer(scratchFramebuffer);
_offscreenContext->doneCurrent(); batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLOR0, glm::vec4(0.0f, 0.0f, 0.0f, 0.0f));
batch.blit(primaryFramebuffer, rect, scratchFramebuffer, rect);
batch.setFramebuffer(nullptr);
});
auto finalTexturePointer = scratchFramebuffer->getRenderBuffer(0);
GLuint finalTexture = gpu::GLBackend::getTextureID(finalTexturePointer);
Q_ASSERT(0 != finalTexture);
Q_ASSERT(!_lockedFramebufferMap.contains(finalTexture));
_lockedFramebufferMap[finalTexture] = scratchFramebuffer;
// Switches to the display plugin context
displayPlugin->preDisplay();
// Ensure all operations from the previous context are complete before we try to read the fbo
glWaitSync(sync, 0, GL_TIMEOUT_IGNORED);
glDeleteSync(sync);
uint64_t displayStart = usecTimestampNow(); uint64_t displayStart = usecTimestampNow();
Q_ASSERT(QOpenGLContext::currentContext() == _offscreenContext->getContext());
{ {
PROFILE_RANGE(__FUNCTION__ "/pluginDisplay"); PROFILE_RANGE(__FUNCTION__ "/pluginSubmitScene");
PerformanceTimer perfTimer("pluginDisplay"); PerformanceTimer perfTimer("pluginSubmitScene");
displayPlugin->display(finalTexture, toGlm(size)); displayPlugin->submitSceneTexture(_frameCount, finalTexture, toGlm(size));
} }
Q_ASSERT(QOpenGLContext::currentContext() == _offscreenContext->getContext());
{
PROFILE_RANGE(__FUNCTION__ "/bufferSwap");
PerformanceTimer perfTimer("bufferSwap");
displayPlugin->finishFrame();
}
uint64_t displayEnd = usecTimestampNow(); uint64_t displayEnd = usecTimestampNow();
const float displayPeriodUsec = (float)(displayEnd - displayStart); // usecs const float displayPeriodUsec = (float)(displayEnd - displayStart); // usecs
_lastPaintWait = displayPeriodUsec / (float)USECS_PER_SECOND; _lastPaintWait = displayPeriodUsec / (float)USECS_PER_SECOND;
} }
{ {
PerformanceTimer perfTimer("makeCurrent");
_offscreenContext->makeCurrent();
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::doInBatch(renderArgs._context, [=](gpu::Batch& batch) { gpu::doInBatch(renderArgs._context, [=](gpu::Batch& batch) {
@ -2612,7 +2619,7 @@ void Application::updateMyAvatarLookAtPosition() {
lookAtPosition.x = -lookAtPosition.x; lookAtPosition.x = -lookAtPosition.x;
} }
if (isHMD) { if (isHMD) {
glm::mat4 headPose = getActiveDisplayPlugin()->getHeadPose(); glm::mat4 headPose = getActiveDisplayPlugin()->getHeadPose(_frameCount);
glm::quat hmdRotation = glm::quat_cast(headPose); glm::quat hmdRotation = glm::quat_cast(headPose);
lookAtSpot = _myCamera.getPosition() + myAvatar->getOrientation() * (hmdRotation * lookAtPosition); lookAtSpot = _myCamera.getPosition() + myAvatar->getOrientation() * (hmdRotation * lookAtPosition);
} else { } else {
@ -4508,7 +4515,7 @@ void Application::takeSnapshot() {
player->setMedia(QUrl::fromLocalFile(inf.absoluteFilePath())); player->setMedia(QUrl::fromLocalFile(inf.absoluteFilePath()));
player->play(); player->play();
QString fileName = Snapshot::saveSnapshot(_glWidget->grabFrameBuffer()); QString fileName = Snapshot::saveSnapshot(getActiveDisplayPlugin()->getScreenshot());
AccountManager& accountManager = AccountManager::getInstance(); AccountManager& accountManager = AccountManager::getInstance();
if (!accountManager.isLoggedIn()) { if (!accountManager.isLoggedIn()) {
@ -4519,7 +4526,6 @@ void Application::takeSnapshot() {
_snapshotShareDialog = new SnapshotShareDialog(fileName, _glWidget); _snapshotShareDialog = new SnapshotShareDialog(fileName, _glWidget);
} }
_snapshotShareDialog->show(); _snapshotShareDialog->show();
} }
float Application::getRenderResolutionScale() const { float Application::getRenderResolutionScale() const {
@ -4702,10 +4708,6 @@ const DisplayPlugin* Application::getActiveDisplayPlugin() const {
return ((Application*)this)->getActiveDisplayPlugin(); return ((Application*)this)->getActiveDisplayPlugin();
} }
bool _activatingDisplayPlugin{ false };
QVector<QPair<QString, QString>> _currentDisplayPluginActions;
QVector<QPair<QString, QString>> _currentInputPluginActions;
static void addDisplayPluginToMenu(DisplayPluginPointer displayPlugin, bool active = false) { static void addDisplayPluginToMenu(DisplayPluginPointer displayPlugin, bool active = false) {
auto menu = Menu::getInstance(); auto menu = Menu::getInstance();
QString name = displayPlugin->getName(); QString name = displayPlugin->getName();
@ -4735,9 +4737,10 @@ void Application::updateDisplayMode() {
bool first = true; bool first = true;
foreach(auto displayPlugin, displayPlugins) { foreach(auto displayPlugin, displayPlugins) {
addDisplayPluginToMenu(displayPlugin, first); addDisplayPluginToMenu(displayPlugin, first);
QObject::connect(displayPlugin.get(), &DisplayPlugin::requestRender, [this] { // This must be a queued connection to avoid a deadlock
paintGL(); QObject::connect(displayPlugin.get(), &DisplayPlugin::requestRender,
}); this, &Application::paintGL, Qt::QueuedConnection);
QObject::connect(displayPlugin.get(), &DisplayPlugin::recommendedFramebufferSizeChanged, [this](const QSize & size) { QObject::connect(displayPlugin.get(), &DisplayPlugin::recommendedFramebufferSizeChanged, [this](const QSize & size) {
resizeGL(); resizeGL();
}); });
@ -4779,19 +4782,18 @@ void Application::updateDisplayMode() {
return; return;
} }
if (!_currentDisplayPluginActions.isEmpty()) {
if (!_pluginContainer->currentDisplayActions().isEmpty()) {
auto menu = Menu::getInstance(); auto menu = Menu::getInstance();
foreach(auto itemInfo, _currentDisplayPluginActions) { foreach(auto itemInfo, _pluginContainer->currentDisplayActions()) {
menu->removeMenuItem(itemInfo.first, itemInfo.second); menu->removeMenuItem(itemInfo.first, itemInfo.second);
} }
_currentDisplayPluginActions.clear(); _pluginContainer->currentDisplayActions().clear();
} }
if (newDisplayPlugin) { if (newDisplayPlugin) {
_offscreenContext->makeCurrent(); _offscreenContext->makeCurrent();
_activatingDisplayPlugin = true;
newDisplayPlugin->activate(); newDisplayPlugin->activate();
_activatingDisplayPlugin = false;
_offscreenContext->makeCurrent(); _offscreenContext->makeCurrent();
offscreenUi->resize(fromGlm(newDisplayPlugin->getRecommendedUiSize())); offscreenUi->resize(fromGlm(newDisplayPlugin->getRecommendedUiSize()));
_offscreenContext->makeCurrent(); _offscreenContext->makeCurrent();
@ -4917,7 +4919,7 @@ mat4 Application::getEyeOffset(int eye) const {
mat4 Application::getHMDSensorPose() const { mat4 Application::getHMDSensorPose() const {
if (isHMDMode()) { if (isHMDMode()) {
return getActiveDisplayPlugin()->getHeadPose(); return getActiveDisplayPlugin()->getHeadPose(_frameCount);
} }
return mat4(); return mat4();
} }

View file

@ -158,6 +158,7 @@ public:
bool isForeground() const { return _isForeground; } bool isForeground() const { return _isForeground; }
uint32_t getFrameCount() { return _frameCount; }
float getFps() const { return _fps; } float getFps() const { return _fps; }
float const HMD_TARGET_FRAME_RATE = 75.0f; float const HMD_TARGET_FRAME_RATE = 75.0f;
float const DESKTOP_TARGET_FRAME_RATE = 60.0f; float const DESKTOP_TARGET_FRAME_RATE = 60.0f;
@ -425,6 +426,9 @@ private:
DisplayPluginPointer _displayPlugin; DisplayPluginPointer _displayPlugin;
InputPluginList _activeInputPlugins; InputPluginList _activeInputPlugins;
bool _activatingDisplayPlugin { false };
QMap<uint32_t, gpu::FramebufferPointer> _lockedFramebufferMap;
MainWindow* _window; MainWindow* _window;
ToolWindow* _toolWindow; ToolWindow* _toolWindow;

View file

@ -13,27 +13,6 @@
#include "Application.h" #include "Application.h"
#include "GLCanvas.h" #include "GLCanvas.h"
#include <QWindow>
#include "MainWindow.h"
#include "Menu.h"
void GLCanvas::paintGL() {
PROFILE_RANGE(__FUNCTION__);
// FIXME - I'm not sure why this still remains, it appears as if this GLCanvas gets a single paintGL call near
// the beginning of the application starting up. I'm not sure if we really need to call Application::paintGL()
// in this case, since the display plugins eventually handle all the painting
bool isThrottleFPSEnabled = Menu::getInstance()->isOptionChecked(MenuOption::ThrottleFPSIfNotFocus);
if (!qApp->getWindow()->isMinimized() || !isThrottleFPSEnabled) {
qApp->paintGL();
}
}
void GLCanvas::resizeGL(int width, int height) {
qApp->resizeGL();
}
bool GLCanvas::event(QEvent* event) { bool GLCanvas::event(QEvent* event) {
if (QEvent::Paint == event->type() && qApp->isAboutToQuit()) { if (QEvent::Paint == event->type() && qApp->isAboutToQuit()) {
return true; return true;

View file

@ -18,8 +18,6 @@
class GLCanvas : public GLWidget { class GLCanvas : public GLWidget {
Q_OBJECT Q_OBJECT
protected: protected:
virtual void paintGL() override;
virtual void resizeGL(int width, int height) override;
virtual bool event(QEvent* event) override; virtual bool event(QEvent* event) override;
}; };

View file

@ -1,17 +1,22 @@
#include "PluginContainerProxy.h" #include "PluginContainerProxy.h"
#include <QScreen> #include <QtGui/QScreen>
#include <QWindow> #include <QtGui/QWindow>
#include <plugins/Plugin.h> #include <plugins/Plugin.h>
#include <plugins/PluginManager.h> #include <plugins/PluginManager.h>
#include <display-plugins/DisplayPlugin.h> #include <display-plugins/DisplayPlugin.h>
#include <DependencyManager.h>
#include <FramebufferCache.h>
#include "Application.h" #include "Application.h"
#include "MainWindow.h" #include "MainWindow.h"
#include "GLCanvas.h" #include "GLCanvas.h"
#include "ui/DialogsManager.h" #include "ui/DialogsManager.h"
#include <gl/OffscreenGLCanvas.h>
#include <QtGui/QOpenGLContext>
PluginContainerProxy::PluginContainerProxy() { PluginContainerProxy::PluginContainerProxy() {
} }
@ -30,12 +35,7 @@ void PluginContainerProxy::removeMenu(const QString& menuName) {
Menu::getInstance()->removeMenu(menuName); Menu::getInstance()->removeMenu(menuName);
} }
extern bool _activatingDisplayPlugin; QAction* PluginContainerProxy::addMenuItem(PluginType type, const QString& path, const QString& name, std::function<void(bool)> onClicked, bool checkable, bool checked, const QString& groupName) {
extern QVector<QPair<QString, QString>> _currentDisplayPluginActions;
extern QVector<QPair<QString, QString>> _currentInputPluginActions;
std::map<QString, QActionGroup*> _exclusiveGroups;
QAction* PluginContainerProxy::addMenuItem(const QString& path, const QString& name, std::function<void(bool)> onClicked, bool checkable, bool checked, const QString& groupName) {
auto menu = Menu::getInstance(); auto menu = Menu::getInstance();
MenuWrapper* parentItem = menu->getMenu(path); MenuWrapper* parentItem = menu->getMenu(path);
QAction* action = menu->addActionToQMenuAndActionHash(parentItem, name); QAction* action = menu->addActionToQMenuAndActionHash(parentItem, name);
@ -54,7 +54,7 @@ QAction* PluginContainerProxy::addMenuItem(const QString& path, const QString& n
}); });
action->setCheckable(checkable); action->setCheckable(checkable);
action->setChecked(checked); action->setChecked(checked);
if (_activatingDisplayPlugin) { if (type == PluginType::DISPLAY_PLUGIN) {
_currentDisplayPluginActions.push_back({ path, name }); _currentDisplayPluginActions.push_back({ path, name });
} else { } else {
_currentInputPluginActions.push_back({ path, name }); _currentInputPluginActions.push_back({ path, name });
@ -150,10 +150,37 @@ void PluginContainerProxy::showDisplayPluginsTools() {
DependencyManager::get<DialogsManager>()->hmdTools(true); DependencyManager::get<DialogsManager>()->hmdTools(true);
} }
QGLWidget* PluginContainerProxy::getPrimarySurface() { GLWidget* PluginContainerProxy::getPrimaryWidget() {
return qApp->_glWidget; return qApp->_glWidget;
} }
QWindow* PluginContainerProxy::getPrimaryWindow() {
return qApp->_glWidget->windowHandle();
}
QOpenGLContext* PluginContainerProxy::getPrimaryContext() {
return qApp->_glWidget->context()->contextHandle();
}
const DisplayPlugin* PluginContainerProxy::getActiveDisplayPlugin() const { const DisplayPlugin* PluginContainerProxy::getActiveDisplayPlugin() const {
return qApp->getActiveDisplayPlugin(); return qApp->getActiveDisplayPlugin();
} }
bool PluginContainerProxy::makeRenderingContextCurrent() {
return qApp->_offscreenContext->makeCurrent();
}
void PluginContainerProxy::releaseSceneTexture(uint32_t texture) {
Q_ASSERT(QThread::currentThread() == qApp->thread());
auto& framebufferMap = qApp->_lockedFramebufferMap;
Q_ASSERT(framebufferMap.contains(texture));
auto framebufferPointer = framebufferMap[texture];
framebufferMap.remove(texture);
auto framebufferCache = DependencyManager::get<FramebufferCache>();
framebufferCache->releaseFramebuffer(framebufferPointer);
}
void PluginContainerProxy::releaseOverlayTexture(uint32_t texture) {
// FIXME implement present thread compositing
}

View file

@ -2,19 +2,21 @@
#ifndef hifi_PluginContainerProxy_h #ifndef hifi_PluginContainerProxy_h
#define hifi_PluginContainerProxy_h #define hifi_PluginContainerProxy_h
#include <QObject> #include <QtCore/QObject>
#include <QRect> #include <QtCore/QRect>
#include <plugins/Forward.h> #include <plugins/Forward.h>
#include <plugins/PluginContainer.h> #include <plugins/PluginContainer.h>
class QActionGroup;
class PluginContainerProxy : public QObject, PluginContainer { class PluginContainerProxy : public QObject, PluginContainer {
Q_OBJECT Q_OBJECT
PluginContainerProxy(); PluginContainerProxy();
virtual ~PluginContainerProxy(); virtual ~PluginContainerProxy();
virtual void addMenu(const QString& menuName) override; virtual void addMenu(const QString& menuName) override;
virtual void removeMenu(const QString& menuName) override; virtual void removeMenu(const QString& menuName) override;
virtual QAction* addMenuItem(const QString& path, const QString& name, std::function<void(bool)> onClicked, bool checkable = false, bool checked = false, const QString& groupName = "") override; virtual QAction* addMenuItem(PluginType type, const QString& path, const QString& name, std::function<void(bool)> onClicked, bool checkable = false, bool checked = false, const QString& groupName = "") override;
virtual void removeMenuItem(const QString& menuName, const QString& menuItem) override; virtual void removeMenuItem(const QString& menuName, const QString& menuItem) override;
virtual bool isOptionChecked(const QString& name) override; virtual bool isOptionChecked(const QString& name) override;
virtual void setIsOptionChecked(const QString& path, bool checked) override; virtual void setIsOptionChecked(const QString& path, bool checked) override;
@ -22,13 +24,20 @@ class PluginContainerProxy : public QObject, PluginContainer {
virtual void unsetFullscreen(const QScreen* avoidScreen = nullptr) override; virtual void unsetFullscreen(const QScreen* avoidScreen = nullptr) override;
virtual void showDisplayPluginsTools() override; virtual void showDisplayPluginsTools() override;
virtual void requestReset() override; virtual void requestReset() override;
virtual QGLWidget* getPrimarySurface() override; virtual bool makeRenderingContextCurrent() override;
virtual void releaseSceneTexture(uint32_t texture) override;
virtual void releaseOverlayTexture(uint32_t texture) override;
virtual GLWidget* getPrimaryWidget() override;
virtual QWindow* getPrimaryWindow() override;
virtual QOpenGLContext* getPrimaryContext() override;
virtual bool isForeground() override; virtual bool isForeground() override;
virtual const DisplayPlugin* getActiveDisplayPlugin() const override; virtual const DisplayPlugin* getActiveDisplayPlugin() const override;
QRect _savedGeometry{ 10, 120, 800, 600 }; QRect _savedGeometry{ 10, 120, 800, 600 };
std::map<QString, QActionGroup*> _exclusiveGroups;
friend class Application; friend class Application;
}; };
#endif #endif

View file

@ -30,7 +30,8 @@ void AvatarUpdate::synchronousProcess() {
// Keep our own updated value, so that our asynchronous code can consult it. // Keep our own updated value, so that our asynchronous code can consult it.
_isHMDMode = qApp->isHMDMode(); _isHMDMode = qApp->isHMDMode();
_headPose = qApp->getActiveDisplayPlugin()->getHeadPose(); auto frameCount = qApp->getFrameCount();
_headPose = qApp->getActiveDisplayPlugin()->getHeadPose(frameCount);
if (_updateBillboard) { if (_updateBillboard) {
DependencyManager::get<AvatarManager>()->getMyAvatar()->doUpdateBillboard(); DependencyManager::get<AvatarManager>()->getMyAvatar()->doUpdateBillboard();

View file

@ -1200,7 +1200,7 @@ void MyAvatar::renderBody(RenderArgs* renderArgs, ViewFrustum* renderFrustum, fl
if (qApp->isHMDMode()) { if (qApp->isHMDMode()) {
glm::vec3 cameraPosition = qApp->getCamera()->getPosition(); glm::vec3 cameraPosition = qApp->getCamera()->getPosition();
glm::mat4 headPose = qApp->getActiveDisplayPlugin()->getHeadPose(); glm::mat4 headPose = qApp->getActiveDisplayPlugin()->getHeadPose(qApp->getFrameCount());
glm::mat4 leftEyePose = qApp->getActiveDisplayPlugin()->getEyeToHeadTransform(Eye::Left); glm::mat4 leftEyePose = qApp->getActiveDisplayPlugin()->getEyeToHeadTransform(Eye::Left);
leftEyePose = leftEyePose * headPose; leftEyePose = leftEyePose * headPose;
glm::vec3 leftEyePosition = extractTranslation(leftEyePose); glm::vec3 leftEyePosition = extractTranslation(leftEyePose);

View file

@ -287,7 +287,7 @@ void ApplicationCompositor::displayOverlayTextureHmd(RenderArgs* renderArgs, int
mat4 camMat; mat4 camMat;
_cameraBaseTransform.getMatrix(camMat); _cameraBaseTransform.getMatrix(camMat);
auto displayPlugin = qApp->getActiveDisplayPlugin(); auto displayPlugin = qApp->getActiveDisplayPlugin();
auto headPose = displayPlugin->getHeadPose(); auto headPose = displayPlugin->getHeadPose(qApp->getFrameCount());
auto eyeToHead = displayPlugin->getEyeToHeadTransform((Eye)eye); auto eyeToHead = displayPlugin->getEyeToHeadTransform((Eye)eye);
camMat = (headPose * eyeToHead) * camMat; camMat = (headPose * eyeToHead) * camMat;
batch.setViewportTransform(renderArgs->_viewport); batch.setViewportTransform(renderArgs->_viewport);

View file

@ -23,6 +23,7 @@
#include <LODManager.h> #include <LODManager.h>
#include <OffscreenUi.h> #include <OffscreenUi.h>
#include <PerfStat.h> #include <PerfStat.h>
#include <plugins/DisplayPlugin.h>
#include "BandwidthRecorder.h" #include "BandwidthRecorder.h"
#include "Menu.h" #include "Menu.h"
@ -118,7 +119,12 @@ void Stats::updateStats(bool force) {
STAT_UPDATE(avatarRenderableCount, avatarManager->getNumberInRenderRange()); STAT_UPDATE(avatarRenderableCount, avatarManager->getNumberInRenderRange());
STAT_UPDATE(avatarRenderDistance, (int) round(avatarManager->getRenderDistance())); // deliberately truncating STAT_UPDATE(avatarRenderDistance, (int) round(avatarManager->getRenderDistance())); // deliberately truncating
STAT_UPDATE(serverCount, nodeList->size()); STAT_UPDATE(serverCount, nodeList->size());
STAT_UPDATE(framerate, (int)qApp->getFps()); STAT_UPDATE(renderrate, (int)qApp->getFps());
if (qApp->getActiveDisplayPlugin()) {
STAT_UPDATE(presentrate, (int)qApp->getActiveDisplayPlugin()->presentRate());
} else {
STAT_UPDATE(presentrate, -1);
}
STAT_UPDATE(simrate, (int)qApp->getAverageSimsPerSecond()); STAT_UPDATE(simrate, (int)qApp->getAverageSimsPerSecond());
STAT_UPDATE(avatarSimrate, (int)qApp->getAvatarSimrate()); STAT_UPDATE(avatarSimrate, (int)qApp->getAvatarSimrate());

View file

@ -32,7 +32,8 @@ class Stats : public QQuickItem {
Q_PROPERTY(float audioPacketlossDownstream READ getAudioPacketLossDownstream) Q_PROPERTY(float audioPacketlossDownstream READ getAudioPacketLossDownstream)
STATS_PROPERTY(int, serverCount, 0) STATS_PROPERTY(int, serverCount, 0)
STATS_PROPERTY(int, framerate, 0) STATS_PROPERTY(int, renderrate, 0)
STATS_PROPERTY(int, presentrate, 0)
STATS_PROPERTY(int, simrate, 0) STATS_PROPERTY(int, simrate, 0)
STATS_PROPERTY(int, avatarSimrate, 0) STATS_PROPERTY(int, avatarSimrate, 0)
STATS_PROPERTY(int, avatarCount, 0) STATS_PROPERTY(int, avatarCount, 0)
@ -115,7 +116,8 @@ signals:
void expandedChanged(); void expandedChanged();
void timingExpandedChanged(); void timingExpandedChanged();
void serverCountChanged(); void serverCountChanged();
void framerateChanged(); void renderrateChanged();
void presentrateChanged();
void simrateChanged(); void simrateChanged();
void avatarSimrateChanged(); void avatarSimrateChanged();
void avatarCountChanged(); void avatarCountChanged();

View file

@ -30,12 +30,11 @@ const QString& Basic2DWindowOpenGLDisplayPlugin::getName() const {
return NAME; return NAME;
} }
std::vector<QAction*> _framerateActions;
QAction* _vsyncAction{ nullptr };
void Basic2DWindowOpenGLDisplayPlugin::activate() { void Basic2DWindowOpenGLDisplayPlugin::activate() {
WindowOpenGLDisplayPlugin::activate();
_framerateActions.clear(); _framerateActions.clear();
_container->addMenuItem(MENU_PATH(), FULLSCREEN, _container->addMenuItem(PluginType::DISPLAY_PLUGIN, MENU_PATH(), FULLSCREEN,
[this](bool clicked) { [this](bool clicked) {
if (clicked) { if (clicked) {
_container->setFullscreen(getFullscreenTarget()); _container->setFullscreen(getFullscreenTarget());
@ -45,26 +44,24 @@ void Basic2DWindowOpenGLDisplayPlugin::activate() {
}, true, false); }, true, false);
_container->addMenu(FRAMERATE); _container->addMenu(FRAMERATE);
_framerateActions.push_back( _framerateActions.push_back(
_container->addMenuItem(FRAMERATE, FRAMERATE_UNLIMITED, _container->addMenuItem(PluginType::DISPLAY_PLUGIN, FRAMERATE, FRAMERATE_UNLIMITED,
[this](bool) { updateFramerate(); }, true, true, FRAMERATE)); [this](bool) { updateFramerate(); }, true, true, FRAMERATE));
_framerateActions.push_back( _framerateActions.push_back(
_container->addMenuItem(FRAMERATE, FRAMERATE_60, _container->addMenuItem(PluginType::DISPLAY_PLUGIN, FRAMERATE, FRAMERATE_60,
[this](bool) { updateFramerate(); }, true, false, FRAMERATE)); [this](bool) { updateFramerate(); }, true, false, FRAMERATE));
_framerateActions.push_back( _framerateActions.push_back(
_container->addMenuItem(FRAMERATE, FRAMERATE_50, _container->addMenuItem(PluginType::DISPLAY_PLUGIN, FRAMERATE, FRAMERATE_50,
[this](bool) { updateFramerate(); }, true, false, FRAMERATE)); [this](bool) { updateFramerate(); }, true, false, FRAMERATE));
_framerateActions.push_back( _framerateActions.push_back(
_container->addMenuItem(FRAMERATE, FRAMERATE_40, _container->addMenuItem(PluginType::DISPLAY_PLUGIN, FRAMERATE, FRAMERATE_40,
[this](bool) { updateFramerate(); }, true, false, FRAMERATE)); [this](bool) { updateFramerate(); }, true, false, FRAMERATE));
_framerateActions.push_back( _framerateActions.push_back(
_container->addMenuItem(FRAMERATE, FRAMERATE_30, _container->addMenuItem(PluginType::DISPLAY_PLUGIN, FRAMERATE, FRAMERATE_30,
[this](bool) { updateFramerate(); }, true, false, FRAMERATE)); [this](bool) { updateFramerate(); }, true, false, FRAMERATE));
WindowOpenGLDisplayPlugin::activate();
// Vsync detection happens in the parent class activate, so we need to check after that // Vsync detection happens in the parent class activate, so we need to check after that
if (_vsyncSupported) { if (_vsyncSupported) {
_vsyncAction = _container->addMenuItem(MENU_PATH(), VSYNC_ON, [this](bool) {}, true, true); _vsyncAction = _container->addMenuItem(PluginType::DISPLAY_PLUGIN, MENU_PATH(), VSYNC_ON, [this](bool) {}, true, true);
} else { } else {
_vsyncAction = nullptr; _vsyncAction = nullptr;
} }
@ -72,22 +69,20 @@ void Basic2DWindowOpenGLDisplayPlugin::activate() {
updateFramerate(); updateFramerate();
} }
void Basic2DWindowOpenGLDisplayPlugin::deactivate() { void Basic2DWindowOpenGLDisplayPlugin::submitSceneTexture(uint32_t frameIndex, uint32_t sceneTexture, const glm::uvec2& sceneSize) {
WindowOpenGLDisplayPlugin::deactivate();
}
void Basic2DWindowOpenGLDisplayPlugin::display(GLuint sceneTexture, const glm::uvec2& sceneSize) {
if (_vsyncAction) { if (_vsyncAction) {
bool wantVsync = _vsyncAction->isChecked(); _wantVsync = _vsyncAction->isChecked();
bool vsyncEnabed = isVsyncEnabled();
if (vsyncEnabed ^ wantVsync) {
enableVsync(wantVsync);
}
} }
WindowOpenGLDisplayPlugin::display(sceneTexture, sceneSize); WindowOpenGLDisplayPlugin::submitSceneTexture(frameIndex, sceneTexture, sceneSize);
} }
void Basic2DWindowOpenGLDisplayPlugin::internalPresent() {
if (_wantVsync != isVsyncEnabled()) {
enableVsync(_wantVsync);
}
WindowOpenGLDisplayPlugin::internalPresent();
}
int Basic2DWindowOpenGLDisplayPlugin::getDesiredInterval() const { int Basic2DWindowOpenGLDisplayPlugin::getDesiredInterval() const {
static const int THROTTLED_PAINT_TIMER_DELAY_MS = MSECS_PER_SECOND / 15; static const int THROTTLED_PAINT_TIMER_DELAY_MS = MSECS_PER_SECOND / 15;

View file

@ -10,6 +10,8 @@
#include "WindowOpenGLDisplayPlugin.h" #include "WindowOpenGLDisplayPlugin.h"
class QScreen; class QScreen;
class QAction;
class Basic2DWindowOpenGLDisplayPlugin : public WindowOpenGLDisplayPlugin { class Basic2DWindowOpenGLDisplayPlugin : public WindowOpenGLDisplayPlugin {
Q_OBJECT Q_OBJECT
@ -17,9 +19,10 @@ public:
virtual const QString & getName() const override; virtual const QString & getName() const override;
virtual void activate() override; virtual void activate() override;
virtual void deactivate() override;
virtual void display(GLuint sceneTexture, const glm::uvec2& sceneSize) override; virtual void submitSceneTexture(uint32_t frameIndex, uint32_t sceneTexture, const glm::uvec2& sceneSize) override;
virtual void internalPresent() override;
virtual bool isThrottled() const override; virtual bool isThrottled() const override;
@ -31,6 +34,9 @@ private:
void updateFramerate(); void updateFramerate();
static const QString NAME; static const QString NAME;
QScreen* getFullscreenTarget(); QScreen* getFullscreenTarget();
uint32_t _framerateTarget{ 0 }; std::vector<QAction*> _framerateActions;
QAction* _vsyncAction { nullptr };
uint32_t _framerateTarget { 0 };
int _fullscreenTarget{ -1 }; int _fullscreenTarget{ -1 };
bool _wantVsync { true };
}; };

View file

@ -25,8 +25,8 @@ const QString& DisplayPlugin::MENU_PATH() {
DisplayPluginList getDisplayPlugins() { DisplayPluginList getDisplayPlugins() {
DisplayPlugin* PLUGIN_POOL[] = { DisplayPlugin* PLUGIN_POOL[] = {
new Basic2DWindowOpenGLDisplayPlugin(), new Basic2DWindowOpenGLDisplayPlugin(),
#ifdef DEBUG
new NullDisplayPlugin(), new NullDisplayPlugin(),
#ifdef DEBUG
#endif #endif
// Stereo modes // Stereo modes
@ -37,10 +37,10 @@ DisplayPluginList getDisplayPlugins() {
new InterleavedStereoDisplayPlugin(), new InterleavedStereoDisplayPlugin(),
// HMDs // HMDs
#ifdef Q_OS_WIN //#ifdef Q_OS_WIN
// SteamVR SDK // // SteamVR SDK
new OpenVrDisplayPlugin(), // new OpenVrDisplayPlugin(),
#endif //#endif
nullptr nullptr
}; };

View file

@ -9,6 +9,9 @@
// //
#include "NullDisplayPlugin.h" #include "NullDisplayPlugin.h"
#include <QtGui/QImage>
#include <plugins/PluginContainer.h>
const QString NullDisplayPlugin::NAME("NullDisplayPlugin"); const QString NullDisplayPlugin::NAME("NullDisplayPlugin");
const QString & NullDisplayPlugin::getName() const { const QString & NullDisplayPlugin::getName() const {
@ -23,8 +26,16 @@ bool NullDisplayPlugin::hasFocus() const {
return false; return false;
} }
void NullDisplayPlugin::preRender() {} void NullDisplayPlugin::submitSceneTexture(uint32_t frameIndex, uint32_t sceneTexture, const glm::uvec2& sceneSize) {
void NullDisplayPlugin::preDisplay() {} _container->releaseSceneTexture(sceneTexture);
void NullDisplayPlugin::display(uint32_t sceneTexture, const glm::uvec2& sceneSize) {} }
void NullDisplayPlugin::finishFrame() {}
void NullDisplayPlugin::submitOverlayTexture(uint32_t overlayTexture, const glm::uvec2& overlaySize) {
_container->releaseOverlayTexture(overlayTexture);
}
void NullDisplayPlugin::stop() {} void NullDisplayPlugin::stop() {}
QImage NullDisplayPlugin::getScreenshot() const {
return QImage();
}

View file

@ -19,11 +19,9 @@ public:
virtual glm::uvec2 getRecommendedRenderSize() const override; virtual glm::uvec2 getRecommendedRenderSize() const override;
virtual bool hasFocus() const override; virtual bool hasFocus() const override;
virtual void preRender() override; virtual void submitSceneTexture(uint32_t frameIndex, uint32_t sceneTexture, const glm::uvec2& sceneSize) override;
virtual void preDisplay() override; virtual void submitOverlayTexture(uint32_t overlayTexture, const glm::uvec2& overlaySize) override;
virtual void display(uint32_t sceneTexture, const glm::uvec2& sceneSize) override; virtual QImage getScreenshot() const override;
virtual void finishFrame() override;
private: private:
static const QString NAME; static const QString NAME;
}; };

View file

@ -6,74 +6,219 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
// //
#include "OpenGLDisplayPlugin.h" #include "OpenGLDisplayPlugin.h"
#include <QCoreApplication>
#include <condition_variable>
#include <QtCore/QCoreApplication>
#include <QtCore/QThread>
#include <QtCore/QTimer>
#include <QtOpenGL/QGLWidget>
#include <QtGui/QOpenGLContext>
#include <QtGui/QImage>
#include <gl/GLWidget.h>
#include <NumericalConstants.h>
#include <DependencyManager.h>
#include <plugins/PluginContainer.h>
#include <gl/Config.h> #include <gl/Config.h>
#include <gl/GLEscrow.h>
#include <GLMHelpers.h> #include <GLMHelpers.h>
class PresentThread : public QThread, public Dependency {
using Mutex = std::mutex;
using Condition = std::condition_variable;
using Lock = std::unique_lock<Mutex>;
public:
~PresentThread() {
_shutdown = true;
wait();
}
void setNewDisplayPlugin(OpenGLDisplayPlugin* plugin) {
Lock lock(_mutex);
_newPlugin = plugin;
}
void setContext(QGLContext * context) {
// Move the OpenGL context to the present thread
// Extra code because of the widget 'wrapper' context
_context = context;
_context->moveToThread(this);
_context->contextHandle()->moveToThread(this);
}
virtual void run() override {
Q_ASSERT(_context);
while (!_shutdown) {
if (_pendingMainThreadOperation) {
{
Lock lock(_mutex);
// Move the context to the main thread
_context->moveToThread(qApp->thread());
_context->contextHandle()->moveToThread(qApp->thread());
_pendingMainThreadOperation = false;
// Release the main thread to do it's action
_condition.notify_one();
}
{
// Main thread does it's thing while we wait on the lock to release
Lock lock(_mutex);
_condition.wait(lock, [&] { return _finishedMainThreadOperation; });
}
}
// Check before lock
if (_newPlugin != nullptr) {
Lock lock(_mutex);
_context->makeCurrent();
// Check if we have a new plugin to activate
if (_newPlugin != nullptr) {
// Deactivate the old plugin
if (_activePlugin != nullptr) {
_activePlugin->uncustomizeContext();
}
_newPlugin->customizeContext();
_activePlugin = _newPlugin;
_newPlugin = nullptr;
}
_context->doneCurrent();
lock.unlock();
}
// If there's no active plugin, just sleep
if (_activePlugin == nullptr) {
QThread::usleep(100);
continue;
}
// take the latest texture and present it
_context->makeCurrent();
_activePlugin->present();
_context->doneCurrent();
}
_context->doneCurrent();
_context->moveToThread(qApp->thread());
_context->contextHandle()->moveToThread(qApp->thread());
}
void withMainThreadContext(std::function<void()> f) {
// Signal to the thread that there is work to be done on the main thread
Lock lock(_mutex);
_pendingMainThreadOperation = true;
_finishedMainThreadOperation = false;
_condition.wait(lock, [&] { return !_pendingMainThreadOperation; });
_context->makeCurrent();
f();
_context->doneCurrent();
// Move the context back to the presentation thread
_context->moveToThread(this);
_context->contextHandle()->moveToThread(this);
// restore control of the context to the presentation thread and signal
// the end of the operation
_finishedMainThreadOperation = true;
lock.unlock();
_condition.notify_one();
}
private:
void makeCurrent();
void doneCurrent();
bool _shutdown { false };
Mutex _mutex;
// Used to allow the main thread to perform context operations
Condition _condition;
bool _pendingMainThreadOperation { false };
bool _finishedMainThreadOperation { false };
QThread* _mainThread { nullptr };
OpenGLDisplayPlugin* _newPlugin { nullptr };
OpenGLDisplayPlugin* _activePlugin { nullptr };
QGLContext* _context { nullptr };
};
OpenGLDisplayPlugin::OpenGLDisplayPlugin() { OpenGLDisplayPlugin::OpenGLDisplayPlugin() {
_sceneTextureEscrow.setRecycler([this](GLuint texture){
cleanupForSceneTexture(texture);
_container->releaseSceneTexture(texture);
});
_overlayTextureEscrow.setRecycler([this](GLuint texture) {
_container->releaseOverlayTexture(texture);
});
connect(&_timer, &QTimer::timeout, this, [&] { connect(&_timer, &QTimer::timeout, this, [&] {
if (_active) { if (_active && _sceneTextureEscrow.depth() < 1) {
emit requestRender(); emit requestRender();
} }
}); });
} }
OpenGLDisplayPlugin::~OpenGLDisplayPlugin() { void OpenGLDisplayPlugin::cleanupForSceneTexture(uint32_t sceneTexture) {
Lock lock(_mutex);
Q_ASSERT(_sceneTextureToFrameIndexMap.contains(sceneTexture));
_sceneTextureToFrameIndexMap.remove(sceneTexture);
} }
void OpenGLDisplayPlugin::preDisplay() {
makeCurrent();
};
void OpenGLDisplayPlugin::preRender() {
// NOOP
}
void OpenGLDisplayPlugin::finishFrame() {
swapBuffers();
doneCurrent();
};
void OpenGLDisplayPlugin::customizeContext() {
using namespace oglplus;
// TODO: write the poper code for linux
#if defined(Q_OS_WIN)
_vsyncSupported = wglewGetExtension("WGL_EXT_swap_control");
#endif
Context::BlendFunc(BlendFunction::SrcAlpha, BlendFunction::OneMinusSrcAlpha);
Context::Disable(Capability::Blend);
Context::Disable(Capability::DepthTest);
Context::Disable(Capability::CullFace);
_program = loadDefaultShader();
_plane = loadPlane(_program);
enableVsync();
}
void OpenGLDisplayPlugin::activate() { void OpenGLDisplayPlugin::activate() {
DisplayPlugin::activate();
_timer.start(1); _timer.start(1);
_vsyncSupported = _container->getPrimaryWidget()->isVsyncSupported();
// Start the present thread if necessary
auto presentThread = DependencyManager::get<PresentThread>();
if (!presentThread) {
auto widget = _container->getPrimaryWidget();
DependencyManager::set<PresentThread>();
presentThread = DependencyManager::get<PresentThread>();
presentThread->setObjectName("Presentation Thread");
presentThread->setContext(widget->context());
// Start execution
presentThread->start();
}
presentThread->setNewDisplayPlugin(this);
DisplayPlugin::activate();
} }
void OpenGLDisplayPlugin::stop() { void OpenGLDisplayPlugin::stop() {
DisplayPlugin::activate();
_timer.stop(); _timer.stop();
} }
void OpenGLDisplayPlugin::deactivate() { void OpenGLDisplayPlugin::deactivate() {
_active = false;
_timer.stop(); _timer.stop();
DisplayPlugin::deactivate();
}
makeCurrent(); void OpenGLDisplayPlugin::customizeContext() {
Q_ASSERT(0 == glGetError()); auto presentThread = DependencyManager::get<PresentThread>();
Q_ASSERT(thread() == presentThread->thread());
enableVsync();
using namespace oglplus;
Context::BlendFunc(BlendFunction::SrcAlpha, BlendFunction::OneMinusSrcAlpha);
Context::Disable(Capability::Blend);
Context::Disable(Capability::DepthTest);
Context::Disable(Capability::CullFace);
_program = loadDefaultShader();
_plane = loadPlane(_program);
}
void OpenGLDisplayPlugin::uncustomizeContext() {
_program.reset(); _program.reset();
_plane.reset(); _plane.reset();
doneCurrent();
} }
// Pressing Alt (and Meta) key alone activates the menubar because its style inherits the // Pressing Alt (and Meta) key alone activates the menubar because its style inherits the
@ -118,13 +263,66 @@ bool OpenGLDisplayPlugin::eventFilter(QObject* receiver, QEvent* event) {
return false; return false;
} }
void OpenGLDisplayPlugin::display( void OpenGLDisplayPlugin::submitSceneTexture(uint32_t frameIndex, uint32_t sceneTexture, const glm::uvec2& sceneSize) {
GLuint finalTexture, const glm::uvec2& sceneSize) { {
Lock lock(_mutex);
_sceneTextureToFrameIndexMap[sceneTexture] = frameIndex;
}
// Submit it to the presentation thread via escrow
_sceneTextureEscrow.submit(sceneTexture);
}
void OpenGLDisplayPlugin::submitOverlayTexture(GLuint sceneTexture, const glm::uvec2& sceneSize) {
// Submit it to the presentation thread via escrow
_overlayTextureEscrow.submit(sceneTexture);
}
void OpenGLDisplayPlugin::updateTextures() {
_currentSceneTexture = _sceneTextureEscrow.fetchAndRelease(_currentSceneTexture);
_currentOverlayTexture = _overlayTextureEscrow.fetchAndRelease(_currentOverlayTexture);
}
void OpenGLDisplayPlugin::updateFramerate() {
uint64_t now = usecTimestampNow();
static uint64_t lastSwapEnd { now };
uint64_t diff = now - lastSwapEnd;
lastSwapEnd = now;
if (diff != 0) {
Lock lock(_mutex);
_usecsPerFrame.updateAverage(diff);
}
}
void OpenGLDisplayPlugin::internalPresent() {
using namespace oglplus; using namespace oglplus;
uvec2 size = getSurfaceSize(); uvec2 size = getSurfacePixels();
Context::Viewport(size.x, size.y); Context::Viewport(size.x, size.y);
glBindTexture(GL_TEXTURE_2D, finalTexture); Context::Clear().DepthBuffer();
glBindTexture(GL_TEXTURE_2D, _currentSceneTexture);
drawUnitQuad(); drawUnitQuad();
swapBuffers();
updateFramerate();
}
void OpenGLDisplayPlugin::present() {
updateTextures();
if (_currentSceneTexture) {
internalPresent();
updateFramerate();
}
}
float OpenGLDisplayPlugin::presentRate() {
float result { -1.0f };
{
Lock lock(_mutex);
result = _usecsPerFrame.getAverage();
result = 1.0f / result;
result *= USECS_PER_SECOND;
}
return result;
} }
void OpenGLDisplayPlugin::drawUnitQuad() { void OpenGLDisplayPlugin::drawUnitQuad() {
@ -151,3 +349,23 @@ bool OpenGLDisplayPlugin::isVsyncEnabled() {
return true; return true;
#endif #endif
} }
void OpenGLDisplayPlugin::swapBuffers() {
static auto widget = _container->getPrimaryWidget();
widget->swapBuffers();
}
void OpenGLDisplayPlugin::withMainThreadContext(std::function<void()> f) const {
static auto presentThread = DependencyManager::get<PresentThread>();
presentThread->withMainThreadContext(f);
_container->makeRenderingContextCurrent();
}
QImage OpenGLDisplayPlugin::getScreenshot() const {
QImage result;
withMainThreadContext([&] {
static auto widget = _container->getPrimaryWidget();
result = widget->grabFrameBuffer();
});
return result;
}

View file

@ -9,42 +9,79 @@
#include "DisplayPlugin.h" #include "DisplayPlugin.h"
#include <QTimer> #include <QtCore/QTimer>
#include <gl/OglplusHelpers.h>
class GlWindow; #include <GLMHelpers.h>
class QOpenGLContext; #include <SimpleMovingAverage.h>
#include <gl/OglplusHelpers.h>
#include <gl/GLEscrow.h>
class OpenGLDisplayPlugin : public DisplayPlugin { class OpenGLDisplayPlugin : public DisplayPlugin {
protected:
using Mutex = std::recursive_mutex;
using Lock = std::unique_lock<Mutex>;
public: public:
OpenGLDisplayPlugin(); OpenGLDisplayPlugin();
virtual ~OpenGLDisplayPlugin();
virtual void preRender() override;
virtual void preDisplay() override;
virtual void finishFrame() override;
virtual void activate() override; virtual void activate() override;
virtual void deactivate() override; virtual void deactivate() override;
virtual void stop() 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 submitSceneTexture(uint32_t frameIndex, uint32_t sceneTexture, const glm::uvec2& sceneSize) override;
virtual void submitOverlayTexture(uint32_t overlayTexture, const glm::uvec2& overlaySize) override;
virtual float presentRate() override;
virtual glm::uvec2 getRecommendedRenderSize() const override {
return getSurfacePixels();
}
virtual glm::uvec2 getRecommendedUiSize() const override {
return getSurfaceSize();
}
virtual QImage getScreenshot() const override;
protected: protected:
virtual void customizeContext(); friend class PresentThread;
virtual void drawUnitQuad();
virtual glm::uvec2 getSurfaceSize() const = 0;
virtual void makeCurrent() = 0;
virtual void doneCurrent() = 0;
virtual void swapBuffers() = 0;
virtual glm::uvec2 getSurfaceSize() const = 0;
virtual glm::uvec2 getSurfacePixels() const = 0;
// FIXME make thread safe?
virtual bool isVsyncEnabled(); virtual bool isVsyncEnabled();
virtual void enableVsync(bool enable = true); virtual void enableVsync(bool enable = true);
// These functions must only be called on the presentation thread
virtual void customizeContext();
virtual void uncustomizeContext();
virtual void cleanupForSceneTexture(uint32_t sceneTexture);
void withMainThreadContext(std::function<void()> f) const;
void present();
void updateTextures();
void updateFramerate();
void drawUnitQuad();
void swapBuffers();
// Plugin specific functionality to composite the scene and overlay and present the result
virtual void internalPresent();
mutable QTimer _timer; mutable QTimer _timer;
ProgramPtr _program; ProgramPtr _program;
ShapeWrapperPtr _plane; ShapeWrapperPtr _plane;
bool _vsyncSupported{ false };
Mutex _mutex;
SimpleMovingAverage _usecsPerFrame { 10 };
QMap<uint32_t, uint32_t> _sceneTextureToFrameIndexMap;
GLuint _currentSceneTexture { 0 };
GLuint _currentOverlayTexture { 0 };
GLTextureEscrow _overlayTextureEscrow;
GLTextureEscrow _sceneTextureEscrow;
bool _vsyncSupported { false };
}; };

View file

@ -7,18 +7,11 @@
// //
#include "WindowOpenGLDisplayPlugin.h" #include "WindowOpenGLDisplayPlugin.h"
#include <QGLWidget> #include <gl/GLWidget.h>
#include "plugins/PluginContainer.h" #include "plugins/PluginContainer.h"
WindowOpenGLDisplayPlugin::WindowOpenGLDisplayPlugin() { glm::uvec2 WindowOpenGLDisplayPlugin::getSurfacePixels() const {
}
glm::uvec2 WindowOpenGLDisplayPlugin::getRecommendedRenderSize() const {
return getSurfaceSize();
}
glm::uvec2 WindowOpenGLDisplayPlugin::getSurfaceSize() const {
uvec2 result; uvec2 result;
if (_window) { if (_window) {
result = toGlm(_window->geometry().size() * _window->devicePixelRatio()); result = toGlm(_window->geometry().size() * _window->devicePixelRatio());
@ -26,8 +19,7 @@ glm::uvec2 WindowOpenGLDisplayPlugin::getSurfaceSize() const {
return result; return result;
} }
glm::uvec2 WindowOpenGLDisplayPlugin::getSurfaceSize() const {
glm::uvec2 WindowOpenGLDisplayPlugin::getRecommendedUiSize() const {
uvec2 result; uvec2 result;
if (_window) { if (_window) {
result = toGlm(_window->geometry().size()); result = toGlm(_window->geometry().size());
@ -40,11 +32,8 @@ bool WindowOpenGLDisplayPlugin::hasFocus() const {
} }
void WindowOpenGLDisplayPlugin::activate() { void WindowOpenGLDisplayPlugin::activate() {
_window = _container->getPrimaryWidget();
OpenGLDisplayPlugin::activate(); OpenGLDisplayPlugin::activate();
_window = _container->getPrimarySurface();
_window->makeCurrent();
customizeContext();
_window->doneCurrent();
} }
void WindowOpenGLDisplayPlugin::deactivate() { void WindowOpenGLDisplayPlugin::deactivate() {
@ -52,14 +41,3 @@ void WindowOpenGLDisplayPlugin::deactivate() {
_window = nullptr; _window = nullptr;
} }
void WindowOpenGLDisplayPlugin::makeCurrent() {
_window->makeCurrent();
}
void WindowOpenGLDisplayPlugin::doneCurrent() {
_window->doneCurrent();
}
void WindowOpenGLDisplayPlugin::swapBuffers() {
_window->swapBuffers();
}

View file

@ -9,21 +9,17 @@
#include "OpenGLDisplayPlugin.h" #include "OpenGLDisplayPlugin.h"
class QGLWidget; class QWidget;
class WindowOpenGLDisplayPlugin : public OpenGLDisplayPlugin { class WindowOpenGLDisplayPlugin : public OpenGLDisplayPlugin {
public: public:
WindowOpenGLDisplayPlugin();
virtual glm::uvec2 getRecommendedRenderSize() const override;
virtual glm::uvec2 getRecommendedUiSize() const override;
virtual bool hasFocus() const override; virtual bool hasFocus() const override;
virtual void activate() override; virtual void activate() override;
virtual void deactivate() override; virtual void deactivate() override;
protected: protected:
virtual glm::uvec2 getSurfaceSize() const override final; virtual glm::uvec2 getSurfaceSize() const override final;
virtual void makeCurrent() override; virtual glm::uvec2 getSurfacePixels() const override final;
virtual void doneCurrent() override;
virtual void swapBuffers() override; QWidget* _window { nullptr };
QGLWidget* _window{ nullptr };
}; };

View file

@ -152,7 +152,7 @@ glm::mat4 OpenVrDisplayPlugin::getEyeToHeadTransform(Eye eye) const {
return _eyesData[eye]._eyeOffset; return _eyesData[eye]._eyeOffset;
} }
glm::mat4 OpenVrDisplayPlugin::getHeadPose() const { glm::mat4 OpenVrDisplayPlugin::getHeadPose(uint32_t frameIndex) const {
return _trackedDevicePoseMat4[0]; return _trackedDevicePoseMat4[0];
} }
@ -160,26 +160,26 @@ void OpenVrDisplayPlugin::customizeContext() {
WindowOpenGLDisplayPlugin::customizeContext(); WindowOpenGLDisplayPlugin::customizeContext();
} }
void OpenVrDisplayPlugin::display(GLuint finalTexture, const glm::uvec2& sceneSize) { //void OpenVrDisplayPlugin::display(uint32_t frameIndex, uint32_t finalTexture, const glm::uvec2& sceneSize) {
// Flip y-axis since GL UV coords are backwards. // // Flip y-axis since GL UV coords are backwards.
static vr::Compositor_TextureBounds leftBounds{ 0, 1, 0.5f, 0 }; // static vr::Compositor_TextureBounds leftBounds{ 0, 1, 0.5f, 0 };
static vr::Compositor_TextureBounds rightBounds{ 0.5f, 1, 1, 0 }; // static vr::Compositor_TextureBounds rightBounds{ 0.5f, 1, 1, 0 };
_compositor->Submit(vr::Eye_Left, (void*)finalTexture, &leftBounds); // _compositor->Submit(vr::Eye_Left, (void*)finalTexture, &leftBounds);
_compositor->Submit(vr::Eye_Right, (void*)finalTexture, &rightBounds); // _compositor->Submit(vr::Eye_Right, (void*)finalTexture, &rightBounds);
glFinish(); // glFinish();
} //}
void OpenVrDisplayPlugin::finishFrame() { //void OpenVrDisplayPlugin::finishFrame() {
// swapBuffers(); //// swapBuffers();
doneCurrent(); // doneCurrent();
_compositor->WaitGetPoses(_trackedDevicePose, vr::k_unMaxTrackedDeviceCount); // _compositor->WaitGetPoses(_trackedDevicePose, vr::k_unMaxTrackedDeviceCount);
for (int i = 0; i < vr::k_unMaxTrackedDeviceCount; i++) { // for (int i = 0; i < vr::k_unMaxTrackedDeviceCount; i++) {
_trackedDevicePoseMat4[i] = _sensorResetMat * toGlm(_trackedDevicePose[i].mDeviceToAbsoluteTracking); // _trackedDevicePoseMat4[i] = _sensorResetMat * toGlm(_trackedDevicePose[i].mDeviceToAbsoluteTracking);
} // }
openvr_for_each_eye([&](vr::Hmd_Eye eye) { // openvr_for_each_eye([&](vr::Hmd_Eye eye) {
_eyesData[eye]._pose = _trackedDevicePoseMat4[0]; // _eyesData[eye]._pose = _trackedDevicePoseMat4[0];
}); // });
}; //};
#endif #endif

View file

@ -31,13 +31,11 @@ public:
virtual void resetSensors() override; virtual void resetSensors() override;
virtual glm::mat4 getEyeToHeadTransform(Eye eye) const override; virtual glm::mat4 getEyeToHeadTransform(Eye eye) const override;
virtual glm::mat4 getHeadPose() const override; virtual glm::mat4 getHeadPose(uint32_t frameIndex) const override;
protected: protected:
virtual void display(GLuint finalTexture, const glm::uvec2& sceneSize) override; // virtual void display(uint32_t frameIndex, uint32_t finalTexture, const glm::uvec2& sceneSize) override;
virtual void customizeContext() override; virtual void customizeContext() override;
// Do not perform swap in finish
virtual void finishFrame() override;
private: private:
vr::IVRSystem* _hmd { nullptr }; vr::IVRSystem* _hmd { nullptr };

View file

@ -66,10 +66,10 @@ glm::uvec2 InterleavedStereoDisplayPlugin::getRecommendedRenderSize() const {
return result; return result;
} }
void InterleavedStereoDisplayPlugin::display( void InterleavedStereoDisplayPlugin::internalPresent() {
GLuint finalTexture, const glm::uvec2& sceneSize) {
using namespace oglplus; using namespace oglplus;
_program->Bind(); _program->Bind();
auto sceneSize = getRecommendedRenderSize();
Uniform<ivec2>(*_program, "textureSize").SetValue(sceneSize); Uniform<ivec2>(*_program, "textureSize").SetValue(sceneSize);
WindowOpenGLDisplayPlugin::display(finalTexture, sceneSize); WindowOpenGLDisplayPlugin::internalPresent();
} }

View file

@ -19,7 +19,7 @@ public:
virtual void customizeContext() override; virtual void customizeContext() override;
virtual glm::uvec2 getRecommendedRenderSize() const override; virtual glm::uvec2 getRecommendedRenderSize() const override;
void display(GLuint finalTexture, const glm::uvec2& sceneSize) override; void internalPresent() override;
private: private:
static const QString NAME; static const QString NAME;

View file

@ -74,7 +74,7 @@ void StereoDisplayPlugin::activate() {
if (screen == qApp->primaryScreen()) { if (screen == qApp->primaryScreen()) {
checked = true; checked = true;
} }
auto action = _container->addMenuItem(MENU_PATH(), name, auto action = _container->addMenuItem(PluginType::DISPLAY_PLUGIN, MENU_PATH(), name,
[this](bool clicked) { updateScreen(); }, true, checked, "Screens"); [this](bool clicked) { updateScreen(); }, true, checked, "Screens");
_screenActions[i] = action; _screenActions[i] = action;
} }

View file

@ -76,7 +76,7 @@ public:
} }
}; };
using Mutex = std::mutex; using Mutex = std::recursive_mutex;
using Lock = std::unique_lock<Mutex>; using Lock = std::unique_lock<Mutex>;
using Recycler = std::function<void(T t)>; using Recycler = std::function<void(T t)>;
// deque gives us random access, double ended push & pop and size, all in constant time // deque gives us random access, double ended push & pop and size, all in constant time
@ -130,6 +130,21 @@ public:
return result; return result;
} }
// 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(T oldValue) {
T result = fetch();
if (!result) {
return oldValue;
}
if (oldValue) {
release(oldValue);
}
return result;
}
// If fetch returns a non-zero value, it's the responsibility of the // If fetch returns a non-zero value, it's the responsibility of the
// client to release it at some point // client to release it at some point
void release(T t, GLsync readSync = 0) { void release(T t, GLsync readSync = 0) {
@ -162,7 +177,12 @@ private:
pop(_releases); pop(_releases);
} }
trash.swap(_trash); {
// FIXME I don't think this lock should be necessary, only the submitting thread
// touches the trash
Lock lock(_mutex);
trash.swap(_trash);
}
} }
// FIXME maybe doing a timing on the deleters and warn if it's taking excessive time? // FIXME maybe doing a timing on the deleters and warn if it's taking excessive time?

View file

@ -7,6 +7,7 @@
// Distributed under the Apache License, Version 2.0. // Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
// //
#include <QtGlobal>
#include "GLWidget.h" #include "GLWidget.h"
@ -16,11 +17,14 @@
#include <QtCore/QUrl> #include <QtCore/QUrl>
#include <QtCore/QCoreApplication> #include <QtCore/QCoreApplication>
#include <QtGui/QOpenGLContext>
#include <QtGui/QKeyEvent> #include <QtGui/QKeyEvent>
#include <QtGui/QWindow> #include <QtGui/QWindow>
#include "GLHelpers.h" #include "GLHelpers.h"
GLWidget::GLWidget() : QGLWidget(getDefaultGLFormat()) { GLWidget::GLWidget() : QGLWidget(getDefaultGLFormat()) {
#ifdef Q_OS_LINUX #ifdef Q_OS_LINUX
// Cause GLWidget::eventFilter to be called. // Cause GLWidget::eventFilter to be called.
@ -42,6 +46,12 @@ void GLWidget::initializeGL() {
setAcceptDrops(true); setAcceptDrops(true);
// Note, we *DO NOT* want Qt to automatically swap buffers for us. This results in the "ringing" bug mentioned in WL#19514 when we're throttling the framerate. // Note, we *DO NOT* want Qt to automatically swap buffers for us. This results in the "ringing" bug mentioned in WL#19514 when we're throttling the framerate.
setAutoBufferSwap(false); setAutoBufferSwap(false);
// TODO: write the proper code for linux
makeCurrent();
#if defined(Q_OS_WIN)
_vsyncSupported = context()->contextHandle()->hasExtension("WGL_EXT_swap_control");;
#endif
} }
void GLWidget::paintEvent(QPaintEvent* event) { void GLWidget::paintEvent(QPaintEvent* event) {
@ -112,3 +122,8 @@ bool GLWidget::eventFilter(QObject*, QEvent* event) {
} }
return false; return false;
} }
bool GLWidget::isVsyncSupported() const {
return _vsyncSupported;
}

View file

@ -21,15 +21,19 @@ public:
int getDeviceWidth() const; int getDeviceWidth() const;
int getDeviceHeight() const; int getDeviceHeight() const;
QSize getDeviceSize() const { return QSize(getDeviceWidth(), getDeviceHeight()); } QSize getDeviceSize() const { return QSize(getDeviceWidth(), getDeviceHeight()); }
bool isVsyncSupported() const;
virtual void initializeGL() override;
protected: protected:
virtual void initializeGL() override;
virtual bool event(QEvent* event) override; virtual bool event(QEvent* event) override;
virtual void paintEvent(QPaintEvent* event) override; virtual void paintEvent(QPaintEvent* event) override;
virtual void resizeEvent(QResizeEvent* event) override; virtual void resizeEvent(QResizeEvent* event) override;
private slots: private slots:
virtual bool eventFilter(QObject*, QEvent* event) override; virtual bool eventFilter(QObject*, QEvent* event) override;
private:
bool _vsyncSupported { false };
}; };

View file

@ -68,7 +68,7 @@ void SixenseManager::activate() {
#ifdef HAVE_SIXENSE #ifdef HAVE_SIXENSE
_container->addMenu(MENU_PATH); _container->addMenu(MENU_PATH);
_container->addMenuItem(MENU_PATH, TOGGLE_SMOOTH, _container->addMenuItem(PluginType::INPUT_PLUGIN, MENU_PATH, TOGGLE_SMOOTH,
[this] (bool clicked) { setSixenseFilter(clicked); }, [this] (bool clicked) { setSixenseFilter(clicked); },
true, true); true, true);

View file

@ -60,7 +60,7 @@ void ViveControllerManager::activate() {
InputPlugin::activate(); InputPlugin::activate();
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
_container->addMenu(MENU_PATH); _container->addMenu(MENU_PATH);
_container->addMenuItem(MENU_PATH, RENDER_CONTROLLERS, _container->addMenuItem(PluginType::INPUT_PLUGIN, MENU_PATH, RENDER_CONTROLLERS,
[this] (bool clicked) { this->setRenderControllers(clicked); }, [this] (bool clicked) { this->setRenderControllers(clicked); },
true, true); true, true);

View file

@ -14,6 +14,7 @@
#include <QtCore/QSize> #include <QtCore/QSize>
#include <QtCore/QPoint> #include <QtCore/QPoint>
class QImage;
#include <GLMHelpers.h> #include <GLMHelpers.h>
#include <RegisteredMetaTypes.h> #include <RegisteredMetaTypes.h>
@ -65,29 +66,15 @@ public:
// processing messages in the middle of submitFrame // processing messages in the middle of submitFrame
virtual void stop() = 0; virtual void stop() = 0;
/**
* Called by the application before the frame rendering. Can be used for
* render timing related calls (for instance, the Oculus begin frame timing
* call)
*/
virtual void preRender() = 0;
/**
* Called by the application immediately before calling the display function.
* For OpenGL based plugins, this is the best place to put activate the output
* OpenGL context
*/
virtual void preDisplay() = 0;
/** /**
* Sends the scene texture to the display plugin. * Sends the scene texture to the display plugin.
*/ */
virtual void display(uint32_t sceneTexture, const glm::uvec2& sceneSize) = 0; virtual void submitSceneTexture(uint32_t frameIndex, uint32_t sceneTexture, const glm::uvec2& sceneSize) = 0;
/** /**
* Called by the application immeidately after display. For OpenGL based * Sends the scene texture to the display plugin.
* displays, this is the best place to put the buffer swap */
*/ virtual void submitOverlayTexture(uint32_t overlayTexture, const glm::uvec2& overlaySize) = 0;
virtual void finishFrame() = 0;
// Does the rendering surface have current focus? // Does the rendering surface have current focus?
virtual bool hasFocus() const = 0; virtual bool hasFocus() const = 0;
@ -110,18 +97,21 @@ public:
return baseProjection; return baseProjection;
} }
// Fetch the most recently displayed image as a QImage
virtual QImage getScreenshot() const = 0;
// HMD specific methods // HMD specific methods
// TODO move these into another class? // TODO move these into another class?
virtual glm::mat4 getEyeToHeadTransform(Eye eye) const { virtual glm::mat4 getEyeToHeadTransform(Eye eye) const {
static const glm::mat4 transform; return transform; static const glm::mat4 transform; return transform;
} }
virtual glm::mat4 getHeadPose() const { virtual glm::mat4 getHeadPose(uint32_t frameIndex) const {
static const glm::mat4 pose; return pose; static const glm::mat4 pose; return pose;
} }
// Needed for timewarp style features // Needed for timewarp style features
virtual void setEyeRenderPose(Eye eye, const glm::mat4& pose) { virtual void setEyeRenderPose(uint32_t frameIndex, Eye eye, const glm::mat4& pose) {
// NOOP // NOOP
} }
@ -129,8 +119,8 @@ public:
virtual void abandonCalibration() {} virtual void abandonCalibration() {}
virtual void resetSensors() {} virtual void resetSensors() {}
virtual float devicePixelRatio() { return 1.0; } virtual float devicePixelRatio() { return 1.0f; }
virtual float presentRate() { return -1.0f; }
static const QString& MENU_PATH(); static const QString& MENU_PATH();
signals: signals:

View file

@ -10,6 +10,11 @@
#include <vector> #include <vector>
#include <memory> #include <memory>
enum class PluginType {
DISPLAY_PLUGIN,
INPUT_PLUGIN,
};
class DisplayPlugin; class DisplayPlugin;
class InputPlugin; class InputPlugin;
class Plugin; class Plugin;

View file

@ -8,11 +8,19 @@
#pragma once #pragma once
#include <functional> #include <functional>
#include <stdint.h>
#include <QString> #include <QString>
#include <QtCore/QVector>
#include <QtCore/QPair>
#include "Forward.h"
class QAction; class QAction;
class QGLWidget; class GLWidget;
class QScreen; class QScreen;
class QOpenGLContext;
class QWindow;
class DisplayPlugin; class DisplayPlugin;
class PluginContainer { class PluginContainer {
@ -22,7 +30,7 @@ public:
virtual ~PluginContainer(); virtual ~PluginContainer();
virtual void addMenu(const QString& menuName) = 0; virtual void addMenu(const QString& menuName) = 0;
virtual void removeMenu(const QString& menuName) = 0; virtual void removeMenu(const QString& menuName) = 0;
virtual QAction* addMenuItem(const QString& path, const QString& name, std::function<void(bool)> onClicked, bool checkable = false, bool checked = false, const QString& groupName = "") = 0; virtual QAction* addMenuItem(PluginType pluginType, const QString& path, const QString& name, std::function<void(bool)> onClicked, bool checkable = false, bool checked = false, const QString& groupName = "") = 0;
virtual void removeMenuItem(const QString& menuName, const QString& menuItem) = 0; virtual void removeMenuItem(const QString& menuName, const QString& menuItem) = 0;
virtual bool isOptionChecked(const QString& name) = 0; virtual bool isOptionChecked(const QString& name) = 0;
virtual void setIsOptionChecked(const QString& path, bool checked) = 0; virtual void setIsOptionChecked(const QString& path, bool checked) = 0;
@ -30,7 +38,25 @@ public:
virtual void unsetFullscreen(const QScreen* avoidScreen = nullptr) = 0; virtual void unsetFullscreen(const QScreen* avoidScreen = nullptr) = 0;
virtual void showDisplayPluginsTools() = 0; virtual void showDisplayPluginsTools() = 0;
virtual void requestReset() = 0; virtual void requestReset() = 0;
virtual QGLWidget* getPrimarySurface() = 0; virtual bool makeRenderingContextCurrent() = 0;
virtual void releaseSceneTexture(uint32_t texture) = 0;
virtual void releaseOverlayTexture(uint32_t texture) = 0;
virtual GLWidget* getPrimaryWidget() = 0;
virtual QWindow* getPrimaryWindow() = 0;
virtual QOpenGLContext* getPrimaryContext() = 0;
virtual bool isForeground() = 0; virtual bool isForeground() = 0;
virtual const DisplayPlugin* getActiveDisplayPlugin() const = 0; virtual const DisplayPlugin* getActiveDisplayPlugin() const = 0;
QVector<QPair<QString, QString>>& currentDisplayActions() {
return _currentDisplayPluginActions;
}
QVector<QPair<QString, QString>>& currentInputActions() {
return _currentInputPluginActions;
}
protected:
QVector<QPair<QString, QString>> _currentDisplayPluginActions;
QVector<QPair<QString, QString>> _currentInputPluginActions;
}; };

View file

@ -15,13 +15,6 @@ uvec2 OculusBaseDisplayPlugin::getRecommendedRenderSize() const {
return _desiredFramebufferSize; return _desiredFramebufferSize;
} }
void OculusBaseDisplayPlugin::preRender() {
#if (OVR_MAJOR_VERSION >= 6)
ovrFrameTiming ftiming = ovr_GetFrameTiming(_hmd, _frameIndex);
_trackingState = ovr_GetTrackingState(_hmd, ftiming.DisplayMidpointSeconds);
#endif
}
glm::mat4 OculusBaseDisplayPlugin::getProjection(Eye eye, const glm::mat4& baseProjection) const { glm::mat4 OculusBaseDisplayPlugin::getProjection(Eye eye, const glm::mat4& baseProjection) const {
return _eyeProjections[eye]; return _eyeProjections[eye];
} }
@ -29,7 +22,6 @@ glm::mat4 OculusBaseDisplayPlugin::getProjection(Eye eye, const glm::mat4& baseP
void OculusBaseDisplayPlugin::resetSensors() { void OculusBaseDisplayPlugin::resetSensors() {
#if (OVR_MAJOR_VERSION >= 6) #if (OVR_MAJOR_VERSION >= 6)
ovr_RecenterPose(_hmd); ovr_RecenterPose(_hmd);
preRender();
#endif #endif
} }
@ -37,15 +29,14 @@ glm::mat4 OculusBaseDisplayPlugin::getEyeToHeadTransform(Eye eye) const {
return glm::translate(mat4(), toGlm(_eyeOffsets[eye])); return glm::translate(mat4(), toGlm(_eyeOffsets[eye]));
} }
glm::mat4 OculusBaseDisplayPlugin::getHeadPose() const { glm::mat4 OculusBaseDisplayPlugin::getHeadPose(uint32_t frameIndex) const {
return toGlm(_trackingState.HeadPose.ThePose); #if (OVR_MAJOR_VERSION >= 6)
auto frameTiming = ovr_GetFrameTiming(_hmd, frameIndex);
auto trackingState = ovr_GetTrackingState(_hmd, frameTiming.DisplayMidpointSeconds);
return toGlm(trackingState.HeadPose.ThePose);
#endif
} }
void OculusBaseDisplayPlugin::setEyeRenderPose(Eye eye, const glm::mat4& pose) {
_eyePoses[eye] = ovrPoseFromGlm(pose);
}
bool OculusBaseDisplayPlugin::isSupported() const { bool OculusBaseDisplayPlugin::isSupported() const {
#if (OVR_MAJOR_VERSION >= 6) #if (OVR_MAJOR_VERSION >= 6)
if (!OVR_SUCCESS(ovr_Initialize(nullptr))) { if (!OVR_SUCCESS(ovr_Initialize(nullptr))) {
@ -77,6 +68,7 @@ void OculusBaseDisplayPlugin::deinit() {
} }
void OculusBaseDisplayPlugin::activate() { void OculusBaseDisplayPlugin::activate() {
WindowOpenGLDisplayPlugin::activate();
#if (OVR_MAJOR_VERSION >= 6) #if (OVR_MAJOR_VERSION >= 6)
if (!OVR_SUCCESS(ovr_Initialize(nullptr))) { if (!OVR_SUCCESS(ovr_Initialize(nullptr))) {
qFatal("Could not init OVR"); qFatal("Could not init OVR");
@ -123,8 +115,6 @@ void OculusBaseDisplayPlugin::activate() {
eyeSizes[0].x + eyeSizes[1].x, eyeSizes[0].x + eyeSizes[1].x,
std::max(eyeSizes[0].y, eyeSizes[1].y)); std::max(eyeSizes[0].y, eyeSizes[1].y));
_frameIndex = 0;
if (!OVR_SUCCESS(ovr_ConfigureTracking(_hmd, if (!OVR_SUCCESS(ovr_ConfigureTracking(_hmd,
ovrTrackingCap_Orientation | ovrTrackingCap_Position | ovrTrackingCap_MagYawCorrection, 0))) { ovrTrackingCap_Orientation | ovrTrackingCap_Position | ovrTrackingCap_MagYawCorrection, 0))) {
qFatal("Could not attach to sensor device"); qFatal("Could not attach to sensor device");
@ -145,8 +135,6 @@ void OculusBaseDisplayPlugin::activate() {
qFatal("Could not attach to sensor device"); qFatal("Could not attach to sensor device");
} }
#endif #endif
WindowOpenGLDisplayPlugin::activate();
} }
void OculusBaseDisplayPlugin::deactivate() { void OculusBaseDisplayPlugin::deactivate() {
@ -159,9 +147,6 @@ void OculusBaseDisplayPlugin::deactivate() {
#endif #endif
} }
void OculusBaseDisplayPlugin::display(GLuint finalTexture, const glm::uvec2& sceneSize) {
++_frameIndex;
}
float OculusBaseDisplayPlugin::getIPD() const { float OculusBaseDisplayPlugin::getIPD() const {
float result = OVR_DEFAULT_IPD; float result = OVR_DEFAULT_IPD;

View file

@ -30,24 +30,18 @@ public:
virtual glm::uvec2 getRecommendedUiSize() const override final { return uvec2(1920, 1080); } virtual glm::uvec2 getRecommendedUiSize() const override final { return uvec2(1920, 1080); }
virtual void resetSensors() override final; virtual void resetSensors() override final;
virtual glm::mat4 getEyeToHeadTransform(Eye eye) const override final; virtual glm::mat4 getEyeToHeadTransform(Eye eye) const override final;
virtual glm::mat4 getHeadPose() const override final;
virtual void setEyeRenderPose(Eye eye, const glm::mat4& pose) override final;
virtual float getIPD() const override final; virtual float getIPD() const override final;
virtual glm::mat4 getHeadPose(uint32_t frameIndex) const override;
protected: protected:
virtual void customizeContext() override; virtual void customizeContext() override;
virtual void preRender() override final;
virtual void display(GLuint finalTexture, const glm::uvec2& sceneSize) override;
protected: protected:
ovrPosef _eyePoses[2];
ovrVector3f _eyeOffsets[2]; ovrVector3f _eyeOffsets[2];
mat4 _eyeProjections[3]; mat4 _eyeProjections[3];
mat4 _compositeEyeProjections[2]; mat4 _compositeEyeProjections[2];
uvec2 _desiredFramebufferSize; uvec2 _desiredFramebufferSize;
ovrTrackingState _trackingState;
unsigned int _frameIndex{ 0 };
#if (OVR_MAJOR_VERSION >= 6) #if (OVR_MAJOR_VERSION >= 6)
ovrHmd _hmd; ovrHmd _hmd;

View file

@ -28,13 +28,3 @@ void OculusDebugDisplayPlugin::customizeContext() {
OculusBaseDisplayPlugin::customizeContext(); OculusBaseDisplayPlugin::customizeContext();
enableVsync(false); enableVsync(false);
} }
void OculusDebugDisplayPlugin::display(GLuint finalTexture, const glm::uvec2& sceneSize) {
WindowOpenGLDisplayPlugin::display(finalTexture, sceneSize);
OculusBaseDisplayPlugin::display(finalTexture, sceneSize);
}
void OculusDebugDisplayPlugin::finishFrame() {
swapBuffers();
doneCurrent();
};

View file

@ -15,10 +15,7 @@ public:
virtual bool isSupported() const override; virtual bool isSupported() const override;
protected: protected:
virtual void display(GLuint finalTexture, const glm::uvec2& sceneSize) override;
virtual void customizeContext() override; virtual void customizeContext() override;
// Do not perform swap in finish
virtual void finishFrame() override;
private: private:
static const QString NAME; static const QString NAME;

View file

@ -144,8 +144,7 @@ static const QString MONO_PREVIEW = "Mono Preview";
static const QString FRAMERATE = DisplayPlugin::MENU_PATH() + ">Framerate"; static const QString FRAMERATE = DisplayPlugin::MENU_PATH() + ">Framerate";
void OculusDisplayPlugin::activate() { void OculusDisplayPlugin::activate() {
_container->addMenuItem(PluginType::DISPLAY_PLUGIN, MENU_PATH(), MONO_PREVIEW,
_container->addMenuItem(MENU_PATH(), MONO_PREVIEW,
[this](bool clicked) { [this](bool clicked) {
_monoPreview = clicked; _monoPreview = clicked;
}, true, true); }, true, true);
@ -170,18 +169,19 @@ void OculusDisplayPlugin::customizeContext() {
_enablePreview = !isVsyncEnabled(); _enablePreview = !isVsyncEnabled();
} }
void OculusDisplayPlugin::deactivate() { void OculusDisplayPlugin::uncustomizeContext() {
#if (OVR_MAJOR_VERSION >= 6) #if (OVR_MAJOR_VERSION >= 6)
makeCurrent();
_sceneFbo.reset(); _sceneFbo.reset();
doneCurrent();
#endif #endif
OculusBaseDisplayPlugin::uncustomizeContext();
OculusBaseDisplayPlugin::deactivate();
} }
void OculusDisplayPlugin::display(GLuint finalTexture, const glm::uvec2& sceneSize) { void OculusDisplayPlugin::internalPresent() {
#if (OVR_MAJOR_VERSION >= 6) #if (OVR_MAJOR_VERSION >= 6)
if (!_currentSceneTexture) {
return;
}
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
// controlling vsync // controlling vsync
@ -196,7 +196,7 @@ void OculusDisplayPlugin::display(GLuint finalTexture, const glm::uvec2& sceneSi
} else { } else {
Context::Viewport(windowSize.x, windowSize.y); Context::Viewport(windowSize.x, windowSize.y);
} }
glBindTexture(GL_TEXTURE_2D, finalTexture); glBindTexture(GL_TEXTURE_2D, _currentSceneTexture);
GLenum err = glGetError(); GLenum err = glGetError();
Q_ASSERT(0 == err); Q_ASSERT(0 == err);
drawUnitQuad(); drawUnitQuad();
@ -205,16 +205,24 @@ void OculusDisplayPlugin::display(GLuint finalTexture, const glm::uvec2& sceneSi
_sceneFbo->Bound([&] { _sceneFbo->Bound([&] {
auto size = _sceneFbo->size; auto size = _sceneFbo->size;
Context::Viewport(size.x, size.y); Context::Viewport(size.x, size.y);
glBindTexture(GL_TEXTURE_2D, finalTexture); glBindTexture(GL_TEXTURE_2D, _currentSceneTexture);
GLenum err = glGetError(); GLenum err = glGetError();
drawUnitQuad(); drawUnitQuad();
}); });
ovr_for_each_eye([&](ovrEyeType eye) { uint32_t frameIndex { 0 };
_sceneLayer.RenderPose[eye] = _eyePoses[eye]; EyePoses eyePoses;
}); {
Lock lock(_mutex);
Q_ASSERT(_sceneTextureToFrameIndexMap.contains(_currentSceneTexture));
frameIndex = _sceneTextureToFrameIndexMap[_currentSceneTexture];
Q_ASSERT(_frameEyePoses.contains(frameIndex));
eyePoses = _frameEyePoses[frameIndex];
}
_sceneLayer.RenderPose[ovrEyeType::ovrEye_Left] = eyePoses.first;
_sceneLayer.RenderPose[ovrEyeType::ovrEye_Right] = eyePoses.second;
auto windowSize = toGlm(_window->size());
{ {
ovrViewScaleDesc viewScaleDesc; ovrViewScaleDesc viewScaleDesc;
viewScaleDesc.HmdSpaceToWorldScaleInMeters = 1.0f; viewScaleDesc.HmdSpaceToWorldScaleInMeters = 1.0f;
@ -228,19 +236,26 @@ void OculusDisplayPlugin::display(GLuint finalTexture, const glm::uvec2& sceneSi
} }
} }
_sceneFbo->Increment(); _sceneFbo->Increment();
++_frameIndex;
#endif #endif
}
/* /*
The swapbuffer call here is only required if we want to mirror the content to the screen. The swapbuffer call here is only required if we want to mirror the content to the screen.
However, it should only be done if we can reliably disable v-sync on the mirror surface, However, it should only be done if we can reliably disable v-sync on the mirror surface,
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() {
if (_enablePreview) { if (_enablePreview) {
swapBuffers(); swapBuffers();
} }
doneCurrent(); }
};
void OculusDisplayPlugin::setEyeRenderPose(uint32_t frameIndex, Eye eye, const glm::mat4& pose) {
auto ovrPose = ovrPoseFromGlm(pose);
{
Lock lock(_mutex);
if (eye == Eye::Left) {
_frameEyePoses[frameIndex].first = ovrPose;
} else {
_frameEyePoses[frameIndex].second = ovrPose;
}
}
}

View file

@ -15,22 +15,21 @@ using SwapFboPtr = QSharedPointer<SwapFramebufferWrapper>;
class OculusDisplayPlugin : public OculusBaseDisplayPlugin { class OculusDisplayPlugin : public OculusBaseDisplayPlugin {
public: public:
virtual void activate() override; virtual void activate() override;
virtual void deactivate() override;
virtual const QString & getName() const override; virtual const QString & getName() const override;
virtual void setEyeRenderPose(uint32_t frameIndex, Eye eye, const glm::mat4& pose) override final;
protected: protected:
virtual void display(GLuint finalTexture, const glm::uvec2& sceneSize) override; virtual void internalPresent() override;
virtual void customizeContext() override; virtual void customizeContext() override;
// Do not perform swap in finish virtual void uncustomizeContext() override;
virtual void finishFrame() override;
private: private:
using EyePoses = std::pair<ovrPosef, ovrPosef>;
static const QString NAME; static const QString NAME;
bool _enablePreview { false }; bool _enablePreview { false };
bool _monoPreview { true }; bool _monoPreview { true };
QMap<uint32_t, EyePoses> _frameEyePoses;
#if (OVR_MAJOR_VERSION >= 6)
SwapFboPtr _sceneFbo; SwapFboPtr _sceneFbo;
#endif
}; };

View file

@ -11,7 +11,6 @@
namespace Oculus { namespace Oculus {
ovrHmd _hmd; ovrHmd _hmd;
unsigned int _frameIndex{ 0 };
ovrEyeRenderDesc _eyeRenderDescs[2]; ovrEyeRenderDesc _eyeRenderDescs[2];
ovrPosef _eyePoses[2]; ovrPosef _eyePoses[2];
ovrVector3f _eyeOffsets[2]; ovrVector3f _eyeOffsets[2];

View file

@ -6,7 +6,8 @@
# See the accompanying file LICENSE or http:#www.apache.org/licenses/LICENSE-2.0.html # See the accompanying file LICENSE or http:#www.apache.org/licenses/LICENSE-2.0.html
# #
if (NOT WIN32) #if (NOT WIN32)
if (FALSE)
set(TARGET_NAME oculusLegacy) set(TARGET_NAME oculusLegacy)
setup_hifi_plugin() setup_hifi_plugin()

View file

@ -46,7 +46,6 @@ private:
static const QString NAME; static const QString NAME;
ovrHmd _hmd; ovrHmd _hmd;
unsigned int _frameIndex;
ovrTrackingState _trackingState; ovrTrackingState _trackingState;
ovrEyeRenderDesc _eyeRenderDescs[2]; ovrEyeRenderDesc _eyeRenderDescs[2];
ovrPosef _eyePoses[2]; ovrPosef _eyePoses[2];

View file

@ -83,7 +83,7 @@ public:
virtual ~PluginContainerProxy() {} virtual ~PluginContainerProxy() {}
virtual void addMenu(const QString& menuName) override {} virtual void addMenu(const QString& menuName) override {}
virtual void removeMenu(const QString& menuName) override {} virtual void removeMenu(const QString& menuName) override {}
virtual QAction* addMenuItem(const QString& path, const QString& name, std::function<void(bool)> onClicked, bool checkable = false, bool checked = false, const QString& groupName = "") override { return nullptr; } virtual QAction* addMenuItem(PluginType type, const QString& path, const QString& name, std::function<void(bool)> onClicked, bool checkable = false, bool checked = false, const QString& groupName = "") override { return nullptr; }
virtual void removeMenuItem(const QString& menuName, const QString& menuItem) override {} virtual void removeMenuItem(const QString& menuName, const QString& menuItem) override {}
virtual bool isOptionChecked(const QString& name) override { return false; } virtual bool isOptionChecked(const QString& name) override { return false; }
virtual void setIsOptionChecked(const QString& path, bool checked) override {} virtual void setIsOptionChecked(const QString& path, bool checked) override {}
@ -91,7 +91,12 @@ public:
virtual void unsetFullscreen(const QScreen* avoidScreen = nullptr) override {} virtual void unsetFullscreen(const QScreen* avoidScreen = nullptr) override {}
virtual void showDisplayPluginsTools() override {} virtual void showDisplayPluginsTools() override {}
virtual void requestReset() override {} virtual void requestReset() override {}
virtual QGLWidget* getPrimarySurface() override { return nullptr; } virtual bool makeRenderingContextCurrent() override { return true; }
virtual void releaseSceneTexture(uint32_t texture) override {}
virtual void releaseOverlayTexture(uint32_t texture) override {}
virtual GLWidget* getPrimaryWidget() override { return nullptr; }
virtual QWindow* getPrimaryWindow() override { return nullptr; }
virtual QOpenGLContext* getPrimaryContext() override { return nullptr; }
virtual bool isForeground() override { return true; } virtual bool isForeground() override { return true; }
virtual const DisplayPlugin* getActiveDisplayPlugin() const override { return nullptr; } virtual const DisplayPlugin* getActiveDisplayPlugin() const override { return nullptr; }
}; };