Prototyping threaded present

This commit is contained in:
Brad Davis 2015-11-30 09:35:18 -08:00
parent d9a1563396
commit b9ace94ff1
37 changed files with 600 additions and 387 deletions

View file

@ -45,7 +45,12 @@ Item {
Text {
color: root.fontColor;
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 {
color: root.fontColor;

View file

@ -150,6 +150,8 @@
#include "InterfaceParentFinder.h"
#include <gpu/GLBackend.h>
#include <QtGui/QOpenGLContext>
// ON WIndows PC, NVidia Optimus laptop, we want to enable NVIDIA GPU
// FIXME seems to be broken.
@ -1089,6 +1091,7 @@ void Application::paintGL() {
// update fps once a second
if (now - _lastFramesPerSecondUpdate > USECS_PER_SECOND) {
_fps = _framesPerSecond.getAverage();
qDebug() << QString::number(_fps, 'g', 4);
_lastFramesPerSecondUpdate = now;
}
@ -1136,7 +1139,7 @@ void Application::paintGL() {
_lastInstantaneousFps = instantaneousFps;
auto displayPlugin = getActiveDisplayPlugin();
displayPlugin->preRender();
// FIXME not needed anymore?
_offscreenContext->makeCurrent();
// update the avatar with a fresh HMD pose
@ -1304,6 +1307,13 @@ void Application::paintGL() {
auto baseProjection = renderArgs._viewFrustum->getProjection();
auto hmdInterface = DependencyManager::get<HMDScriptingInterface>();
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,
// 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.
@ -1319,12 +1329,7 @@ void Application::paintGL() {
mat4 eyeOffsetTransform = glm::translate(mat4(), eyeOffset * -1.0f * IPDScale);
eyeOffsets[eye] = eyeOffsetTransform;
// 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();
displayPlugin->setEyeRenderPose(eye, headPose);
displayPlugin->setEyeRenderPose(_frameCount, eye, headPose);
eyeProjections[eye] = displayPlugin->getProjection(eye, baseProjection);
});
@ -1367,44 +1372,35 @@ void Application::paintGL() {
{
PROFILE_RANGE(__FUNCTION__ "/pluginOutput");
PerformanceTimer perfTimer("pluginOutput");
auto primaryFbo = framebufferCache->getPrimaryFramebuffer();
GLuint finalTexture = gpu::GLBackend::getTextureID(primaryFbo->getRenderBuffer(0));
// Ensure the rendering context commands are completed when rendering
GLsync sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
// Ensure the sync object is flushed to the driver thread before releasing the context
// CRITICAL for the mac driver apparently.
glFlush();
_offscreenContext->doneCurrent();
// 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();
auto primaryFramebuffer = framebufferCache->getPrimaryFramebuffer();
auto scratchFramebuffer = framebufferCache->getFramebuffer();
gpu::doInBatch(renderArgs._context, [=](gpu::Batch& batch) {
gpu::Vec4i rect;
rect.z = size.width();
rect.w = size.height();
batch.setFramebuffer(scratchFramebuffer);
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);
{
PROFILE_RANGE(__FUNCTION__ "/pluginDisplay");
PerformanceTimer perfTimer("pluginDisplay");
displayPlugin->display(finalTexture, toGlm(size));
}
Q_ASSERT(0 != finalTexture);
Q_ASSERT(!_lockedFramebufferMap.contains(finalTexture));
_lockedFramebufferMap[finalTexture] = scratchFramebuffer;
Q_ASSERT(QOpenGLContext::currentContext() == _offscreenContext->getContext());
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();
const float displayPeriodUsec = (float)(displayEnd - displayStart); // usecs
_lastPaintWait = displayPeriodUsec / (float)USECS_PER_SECOND;
}
{
PerformanceTimer perfTimer("makeCurrent");
_offscreenContext->makeCurrent();
Stats::getInstance()->setRenderDetails(renderArgs._details);
// Reset the gpu::Context Stages
// Back to the default framebuffer;
gpu::doInBatch(renderArgs._context, [=](gpu::Batch& batch) {
@ -2612,7 +2608,7 @@ void Application::updateMyAvatarLookAtPosition() {
lookAtPosition.x = -lookAtPosition.x;
}
if (isHMD) {
glm::mat4 headPose = getActiveDisplayPlugin()->getHeadPose();
glm::mat4 headPose = getActiveDisplayPlugin()->getHeadPose(_frameCount);
glm::quat hmdRotation = glm::quat_cast(headPose);
lookAtSpot = _myCamera.getPosition() + myAvatar->getOrientation() * (hmdRotation * lookAtPosition);
} else {
@ -4503,6 +4499,7 @@ void Application::toggleLogDialog() {
}
void Application::takeSnapshot() {
#if 0
QMediaPlayer* player = new QMediaPlayer();
QFileInfo inf = QFileInfo(PathUtils::resourcesPath() + "sounds/snap.wav");
player->setMedia(QUrl::fromLocalFile(inf.absoluteFilePath()));
@ -4519,7 +4516,7 @@ void Application::takeSnapshot() {
_snapshotShareDialog = new SnapshotShareDialog(fileName, _glWidget);
}
_snapshotShareDialog->show();
#endif
}
float Application::getRenderResolutionScale() const {
@ -4702,10 +4699,6 @@ const DisplayPlugin* Application::getActiveDisplayPlugin() const {
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) {
auto menu = Menu::getInstance();
QString name = displayPlugin->getName();
@ -4735,9 +4728,9 @@ void Application::updateDisplayMode() {
bool first = true;
foreach(auto displayPlugin, displayPlugins) {
addDisplayPluginToMenu(displayPlugin, first);
QObject::connect(displayPlugin.get(), &DisplayPlugin::requestRender, [this] {
paintGL();
});
QObject::connect(displayPlugin.get(), &DisplayPlugin::requestRender,
this, &Application::paintGL, Qt::QueuedConnection);
QObject::connect(displayPlugin.get(), &DisplayPlugin::recommendedFramebufferSizeChanged, [this](const QSize & size) {
resizeGL();
});
@ -4917,7 +4910,7 @@ mat4 Application::getEyeOffset(int eye) const {
mat4 Application::getHMDSensorPose() const {
if (isHMDMode()) {
return getActiveDisplayPlugin()->getHeadPose();
return getActiveDisplayPlugin()->getHeadPose(_frameCount);
}
return mat4();
}

View file

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

View file

@ -13,27 +13,6 @@
#include "Application.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) {
if (QEvent::Paint == event->type() && qApp->isAboutToQuit()) {
return true;

View file

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

View file

@ -1,17 +1,23 @@
#include "PluginContainerProxy.h"
#include <QScreen>
#include <QWindow>
#include <QtGui/QScreen>
#include <QtGui/QWindow>
#include <plugins/Plugin.h>
#include <plugins/PluginManager.h>
#include <display-plugins/DisplayPlugin.h>
#include <gl/GlWindow.h>
#include <DependencyManager.h>
#include <FramebufferCache.h>
#include "Application.h"
#include "MainWindow.h"
#include "GLCanvas.h"
#include "ui/DialogsManager.h"
#include <gl/OffscreenGLCanvas.h>
#include <QtGui/QOpenGLContext>
PluginContainerProxy::PluginContainerProxy() {
}
@ -36,30 +42,31 @@ 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();
MenuWrapper* parentItem = menu->getMenu(path);
QAction* action = menu->addActionToQMenuAndActionHash(parentItem, name);
if (!groupName.isEmpty()) {
QActionGroup* group{ nullptr };
if (!_exclusiveGroups.count(groupName)) {
group = _exclusiveGroups[groupName] = new QActionGroup(menu);
group->setExclusive(true);
} else {
group = _exclusiveGroups[groupName];
}
group->addAction(action);
}
connect(action, &QAction::triggered, [=] {
onClicked(action->isChecked());
});
action->setCheckable(checkable);
action->setChecked(checked);
if (_activatingDisplayPlugin) {
_currentDisplayPluginActions.push_back({ path, name });
} else {
_currentInputPluginActions.push_back({ path, name });
}
return action;
//auto menu = Menu::getInstance();
//MenuWrapper* parentItem = menu->getMenu(path);
//QAction* action = menu->addActionToQMenuAndActionHash(parentItem, name);
//if (!groupName.isEmpty()) {
// QActionGroup* group{ nullptr };
// if (!_exclusiveGroups.count(groupName)) {
// group = _exclusiveGroups[groupName] = new QActionGroup(menu);
// group->setExclusive(true);
// } else {
// group = _exclusiveGroups[groupName];
// }
// group->addAction(action);
//}
//connect(action, &QAction::triggered, [=] {
// onClicked(action->isChecked());
//});
//action->setCheckable(checkable);
//action->setChecked(checked);
//if (_activatingDisplayPlugin) {
// _currentDisplayPluginActions.push_back({ path, name });
//} else {
// _currentInputPluginActions.push_back({ path, name });
//}
//return action;
return nullptr;
}
void PluginContainerProxy::removeMenuItem(const QString& menuName, const QString& menuItem) {
@ -150,10 +157,36 @@ void PluginContainerProxy::showDisplayPluginsTools() {
DependencyManager::get<DialogsManager>()->hmdTools(true);
}
QGLWidget* PluginContainerProxy::getPrimarySurface() {
QGLWidget* PluginContainerProxy::getPrimaryWidget() {
return qApp->_glWidget;
}
QWindow* PluginContainerProxy::getPrimaryWindow() {
return qApp->_glWidget->windowHandle();
}
QOpenGLContext* PluginContainerProxy::getPrimaryContext() {
return qApp->_glWidget->context()->contextHandle();
}
const DisplayPlugin* PluginContainerProxy::getActiveDisplayPlugin() const {
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) {
}

View file

@ -22,7 +22,12 @@ class PluginContainerProxy : public QObject, PluginContainer {
virtual void unsetFullscreen(const QScreen* avoidScreen = nullptr) override;
virtual void showDisplayPluginsTools() 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 QGLWidget* getPrimaryWidget() override;
virtual QWindow* getPrimaryWindow() override;
virtual QOpenGLContext* getPrimaryContext() override;
virtual bool isForeground() override;
virtual const DisplayPlugin* getActiveDisplayPlugin() const override;

View file

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

View file

@ -1200,7 +1200,7 @@ void MyAvatar::renderBody(RenderArgs* renderArgs, ViewFrustum* renderFrustum, fl
if (qApp->isHMDMode()) {
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);
leftEyePose = leftEyePose * headPose;
glm::vec3 leftEyePosition = extractTranslation(leftEyePose);

View file

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

View file

@ -23,6 +23,7 @@
#include <LODManager.h>
#include <OffscreenUi.h>
#include <PerfStat.h>
#include <plugins/DisplayPlugin.h>
#include "BandwidthRecorder.h"
#include "Menu.h"
@ -118,7 +119,12 @@ void Stats::updateStats(bool force) {
STAT_UPDATE(avatarRenderableCount, avatarManager->getNumberInRenderRange());
STAT_UPDATE(avatarRenderDistance, (int) round(avatarManager->getRenderDistance())); // deliberately truncating
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(avatarSimrate, (int)qApp->getAvatarSimrate());

View file

@ -32,7 +32,8 @@ class Stats : public QQuickItem {
Q_PROPERTY(float audioPacketlossDownstream READ getAudioPacketLossDownstream)
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, avatarSimrate, 0)
STATS_PROPERTY(int, avatarCount, 0)
@ -115,7 +116,8 @@ signals:
void expandedChanged();
void timingExpandedChanged();
void serverCountChanged();
void framerateChanged();
void renderrateChanged();
void presentrateChanged();
void simrateChanged();
void avatarSimrateChanged();
void avatarCountChanged();

View file

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

View file

@ -10,6 +10,8 @@
#include "WindowOpenGLDisplayPlugin.h"
class QScreen;
class QAction;
class Basic2DWindowOpenGLDisplayPlugin : public WindowOpenGLDisplayPlugin {
Q_OBJECT
@ -19,7 +21,7 @@ public:
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 bool isThrottled() const override;
@ -31,6 +33,9 @@ private:
void updateFramerate();
static const QString NAME;
QScreen* getFullscreenTarget();
uint32_t _framerateTarget{ 0 };
std::vector<QAction*> _framerateActions;
QAction* _vsyncAction { nullptr };
uint32_t _framerateTarget { 0 };
int _fullscreenTarget{ -1 };
bool _wantVsync { true };
};

View file

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

View file

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

View file

@ -19,10 +19,8 @@ public:
virtual glm::uvec2 getRecommendedRenderSize() const override;
virtual bool hasFocus() const override;
virtual void preRender() override;
virtual void preDisplay() override;
virtual void display(uint32_t sceneTexture, const glm::uvec2& sceneSize) override;
virtual void finishFrame() 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;
private:
static const QString NAME;

View file

@ -6,71 +6,167 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "OpenGLDisplayPlugin.h"
#include <QCoreApplication>
#include <QtCore/QCoreApplication>
#include <QtCore/QThread>
#include <QtCore/QTimer>
#include <QtOpenGL/QGLWidget>
#include <QtGui/QOpenGLContext>
#include <NumericalConstants.h>
#include <DependencyManager.h>
#include <plugins/PluginContainer.h>
#include <gl/Config.h>
#include <gl/GLEscrow.h>
#include <GLMHelpers.h>
class PresentThread : public QThread, public Dependency {
using Mutex = std::mutex;
using Lock = std::unique_lock<Mutex>;
friend class OpenGLDisplayPlugin;
public:
~PresentThread() {
_shutdown = true;
wait();
}
void setNewDisplayPlugin(OpenGLDisplayPlugin* plugin) {
Lock lock(_mutex);
_newPlugin = plugin;
}
virtual void run() override {
Q_ASSERT(_context);
while (!_shutdown) {
// Check before lock
if (_newPlugin != nullptr) {
Lock lock(_mutex);
// 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
_activePlugin->present();
}
_context->doneCurrent();
_context->moveToThread(qApp->thread());
}
private:
bool _shutdown { false };
Mutex _mutex;
QThread* _mainThread { nullptr };
OpenGLDisplayPlugin* _newPlugin { nullptr };
OpenGLDisplayPlugin* _activePlugin { nullptr };
QOpenGLContext* _context { nullptr };
};
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, [&] {
if (_active) {
if (_active && _sceneTextureEscrow.depth() < 1) {
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() {
_timer.start(2);
// Start the present thread if necessary
auto presentThread = DependencyManager::get<PresentThread>();
if (!presentThread) {
DependencyManager::set<PresentThread>();
presentThread = DependencyManager::get<PresentThread>();
auto widget = _container->getPrimaryWidget();
auto glContext = widget->context();
auto context = glContext->contextHandle();
glContext->moveToThread(presentThread.data());
context->moveToThread(presentThread.data());
// Move the OpenGL context to the present thread
presentThread->_context = context;
// Start execution
presentThread->start();
}
presentThread->setNewDisplayPlugin(this);
DisplayPlugin::activate();
_timer.start(1);
emit requestRender();
}
void OpenGLDisplayPlugin::stop() {
DisplayPlugin::activate();
_timer.stop();
}
void OpenGLDisplayPlugin::deactivate() {
_active = false;
_timer.stop();
DisplayPlugin::deactivate();
}
void OpenGLDisplayPlugin::customizeContext() {
auto presentThread = DependencyManager::get<PresentThread>();
Q_ASSERT(thread() == presentThread->thread());
bool makeCurrentResult = makeCurrent();
Q_ASSERT(makeCurrentResult);
// TODO: write the proper code for linux
#if defined(Q_OS_WIN)
_vsyncSupported = wglewGetExtension("WGL_EXT_swap_control");
#endif
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);
doneCurrent();
}
void OpenGLDisplayPlugin::uncustomizeContext() {
makeCurrent();
Q_ASSERT(0 == glGetError());
_program.reset();
_plane.reset();
doneCurrent();
@ -118,13 +214,74 @@ bool OpenGLDisplayPlugin::eventFilter(QObject* receiver, QEvent* event) {
return false;
}
void OpenGLDisplayPlugin::display(
GLuint finalTexture, const glm::uvec2& sceneSize) {
void OpenGLDisplayPlugin::submitSceneTexture(uint32_t frameIndex, uint32_t sceneTexture, 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;
uvec2 size = getSurfaceSize();
uvec2 size = getSurfacePixels();
Context::Viewport(size.x, size.y);
glBindTexture(GL_TEXTURE_2D, finalTexture);
Context::Clear().DepthBuffer();
glBindTexture(GL_TEXTURE_2D, _currentSceneTexture);
drawUnitQuad();
swapBuffers();
updateFramerate();
}
void OpenGLDisplayPlugin::present() {
auto makeCurrentResult = makeCurrent();
Q_ASSERT(makeCurrentResult);
if (!makeCurrentResult) {
qDebug() << "Failed to make current";
return;
}
updateTextures();
if (_currentSceneTexture) {
internalPresent();
updateFramerate();
}
doneCurrent();
}
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() {
@ -151,3 +308,20 @@ bool OpenGLDisplayPlugin::isVsyncEnabled() {
return true;
#endif
}
bool OpenGLDisplayPlugin::makeCurrent() {
static auto widget = _container->getPrimaryWidget();
widget->makeCurrent();
auto result = widget->context()->contextHandle() == QOpenGLContext::currentContext();
Q_ASSERT(result);
return result;
}
void OpenGLDisplayPlugin::doneCurrent() {
static auto widget = _container->getPrimaryWidget();
widget->doneCurrent();
}
void OpenGLDisplayPlugin::swapBuffers() {
static auto widget = _container->getPrimaryWidget();
widget->swapBuffers();
}

View file

@ -9,42 +9,74 @@
#include "DisplayPlugin.h"
#include <QTimer>
#include <gl/OglplusHelpers.h>
#include <QtCore/QTimer>
class GlWindow;
class QOpenGLContext;
#include <GLMHelpers.h>
#include <SimpleMovingAverage.h>
#include <gl/OglplusHelpers.h>
#include <gl/GLEscrow.h>
class OpenGLDisplayPlugin : public DisplayPlugin {
protected:
using Mutex = std::recursive_mutex;
using Lock = std::unique_lock<Mutex>;
public:
OpenGLDisplayPlugin();
virtual ~OpenGLDisplayPlugin();
virtual void preRender() override;
virtual void preDisplay() override;
virtual void finishFrame() override;
virtual void activate() override;
virtual void deactivate() override;
virtual void stop() 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 {
return getSurfacePixels();
}
virtual glm::uvec2 getRecommendedUiSize() const {
return getSurfaceSize();
}
protected:
virtual void customizeContext();
virtual void drawUnitQuad();
virtual glm::uvec2 getSurfaceSize() const = 0;
virtual void makeCurrent() = 0;
virtual void doneCurrent() = 0;
virtual void swapBuffers() = 0;
friend class PresentThread;
virtual glm::uvec2 getSurfaceSize() const = 0;
virtual glm::uvec2 getSurfacePixels() const = 0;
// FIXME make thread safe?
virtual bool isVsyncEnabled();
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 present();
void updateTextures();
void updateFramerate();
void drawUnitQuad();
bool makeCurrent();
void doneCurrent();
void swapBuffers();
// Plugin specific functionality to composite the scene and overlay and present the result
virtual void internalPresent();
mutable QTimer _timer;
ProgramPtr _program;
ShapeWrapperPtr _plane;
bool _vsyncSupported{ false };
bool _vsyncSupported { false };
Mutex _mutex;
SimpleMovingAverage _usecsPerFrame { 10 };
QMap<uint32_t, uint32_t> _sceneTextureToFrameIndexMap;
GLuint _currentSceneTexture { 0 };
GLuint _currentOverlayTexture { 0 };
GLTextureEscrow _overlayTextureEscrow;
GLTextureEscrow _sceneTextureEscrow;
};

View file

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

View file

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

View file

@ -31,13 +31,11 @@ public:
virtual void resetSensors() 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:
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;
// Do not perform swap in finish
virtual void finishFrame() override;
private:
vr::IVRSystem* _hmd { nullptr };

View file

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

View file

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

View file

@ -76,7 +76,7 @@ public:
}
};
using Mutex = std::mutex;
using Mutex = std::recursive_mutex;
using Lock = std::unique_lock<Mutex>;
using Recycler = std::function<void(T t)>;
// deque gives us random access, double ended push & pop and size, all in constant time
@ -130,6 +130,21 @@ public:
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
// client to release it at some point
void release(T t, GLsync readSync = 0) {
@ -162,7 +177,12 @@ private:
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?

View file

@ -65,29 +65,15 @@ public:
// processing messages in the middle of submitFrame
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.
*/
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
* displays, this is the best place to put the buffer swap
*/
virtual void finishFrame() = 0;
* Sends the scene texture to the display plugin.
*/
virtual void submitOverlayTexture(uint32_t overlayTexture, const glm::uvec2& overlaySize) = 0;
// Does the rendering surface have current focus?
virtual bool hasFocus() const = 0;
@ -116,12 +102,12 @@ public:
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;
}
// 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
}
@ -129,8 +115,8 @@ public:
virtual void abandonCalibration() {}
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();
signals:

View file

@ -8,11 +8,15 @@
#pragma once
#include <functional>
#include <stdint.h>
#include <QString>
class QAction;
class QGLWidget;
class QScreen;
class QOpenGLContext;
class QWindow;
class DisplayPlugin;
class PluginContainer {
@ -30,7 +34,12 @@ public:
virtual void unsetFullscreen(const QScreen* avoidScreen = nullptr) = 0;
virtual void showDisplayPluginsTools() = 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 QGLWidget* getPrimaryWidget() = 0;
virtual QWindow* getPrimaryWindow() = 0;
virtual QOpenGLContext* getPrimaryContext() = 0;
virtual bool isForeground() = 0;
virtual const DisplayPlugin* getActiveDisplayPlugin() const = 0;
};

View file

@ -15,13 +15,6 @@ uvec2 OculusBaseDisplayPlugin::getRecommendedRenderSize() const {
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 {
return _eyeProjections[eye];
}
@ -29,7 +22,6 @@ glm::mat4 OculusBaseDisplayPlugin::getProjection(Eye eye, const glm::mat4& baseP
void OculusBaseDisplayPlugin::resetSensors() {
#if (OVR_MAJOR_VERSION >= 6)
ovr_RecenterPose(_hmd);
preRender();
#endif
}
@ -37,15 +29,14 @@ glm::mat4 OculusBaseDisplayPlugin::getEyeToHeadTransform(Eye eye) const {
return glm::translate(mat4(), toGlm(_eyeOffsets[eye]));
}
glm::mat4 OculusBaseDisplayPlugin::getHeadPose() const {
return toGlm(_trackingState.HeadPose.ThePose);
glm::mat4 OculusBaseDisplayPlugin::getHeadPose(uint32_t frameIndex) const {
#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 {
#if (OVR_MAJOR_VERSION >= 6)
if (!OVR_SUCCESS(ovr_Initialize(nullptr))) {
@ -64,9 +55,11 @@ bool OculusBaseDisplayPlugin::isSupported() const {
// DLL based display plugins MUST initialize GLEW inside the DLL code.
void OculusBaseDisplayPlugin::customizeContext() {
makeCurrent();
glewExperimental = true;
GLenum err = glewInit();
glGetError();
doneCurrent();
WindowOpenGLDisplayPlugin::customizeContext();
}
@ -123,8 +116,6 @@ void OculusBaseDisplayPlugin::activate() {
eyeSizes[0].x + eyeSizes[1].x,
std::max(eyeSizes[0].y, eyeSizes[1].y));
_frameIndex = 0;
if (!OVR_SUCCESS(ovr_ConfigureTracking(_hmd,
ovrTrackingCap_Orientation | ovrTrackingCap_Position | ovrTrackingCap_MagYawCorrection, 0))) {
qFatal("Could not attach to sensor device");
@ -159,9 +150,6 @@ void OculusBaseDisplayPlugin::deactivate() {
#endif
}
void OculusBaseDisplayPlugin::display(GLuint finalTexture, const glm::uvec2& sceneSize) {
++_frameIndex;
}
float OculusBaseDisplayPlugin::getIPD() const {
float result = OVR_DEFAULT_IPD;

View file

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

View file

@ -28,13 +28,3 @@ void OculusDebugDisplayPlugin::customizeContext() {
OculusBaseDisplayPlugin::customizeContext();
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;
protected:
virtual void display(GLuint finalTexture, const glm::uvec2& sceneSize) override;
virtual void customizeContext() override;
// Do not perform swap in finish
virtual void finishFrame() override;
private:
static const QString NAME;

View file

@ -144,7 +144,6 @@ static const QString MONO_PREVIEW = "Mono Preview";
static const QString FRAMERATE = DisplayPlugin::MENU_PATH() + ">Framerate";
void OculusDisplayPlugin::activate() {
_container->addMenuItem(MENU_PATH(), MONO_PREVIEW,
[this](bool clicked) {
_monoPreview = clicked;
@ -155,6 +154,8 @@ void OculusDisplayPlugin::activate() {
void OculusDisplayPlugin::customizeContext() {
OculusBaseDisplayPlugin::customizeContext();
bool makeCurrentResult = makeCurrent();
Q_ASSERT(makeCurrentResult);
#if (OVR_MAJOR_VERSION >= 6)
_sceneFbo = SwapFboPtr(new SwapFramebufferWrapper(_hmd));
_sceneFbo->Init(getRecommendedRenderSize());
@ -168,20 +169,24 @@ void OculusDisplayPlugin::customizeContext() {
enableVsync(false);
// Only enable mirroring if we know vsync is disabled
_enablePreview = !isVsyncEnabled();
doneCurrent();
}
void OculusDisplayPlugin::deactivate() {
void OculusDisplayPlugin::uncustomizeContext() {
#if (OVR_MAJOR_VERSION >= 6)
makeCurrent();
_sceneFbo.reset();
doneCurrent();
#endif
OculusBaseDisplayPlugin::deactivate();
OculusBaseDisplayPlugin::uncustomizeContext();
}
void OculusDisplayPlugin::display(GLuint finalTexture, const glm::uvec2& sceneSize) {
void OculusDisplayPlugin::internalPresent() {
#if (OVR_MAJOR_VERSION >= 6)
if (!_currentSceneTexture) {
return;
}
using namespace oglplus;
// Need to make sure only the display plugin is responsible for
// controlling vsync
@ -196,7 +201,7 @@ void OculusDisplayPlugin::display(GLuint finalTexture, const glm::uvec2& sceneSi
} else {
Context::Viewport(windowSize.x, windowSize.y);
}
glBindTexture(GL_TEXTURE_2D, finalTexture);
glBindTexture(GL_TEXTURE_2D, _currentSceneTexture);
GLenum err = glGetError();
Q_ASSERT(0 == err);
drawUnitQuad();
@ -205,16 +210,24 @@ void OculusDisplayPlugin::display(GLuint finalTexture, const glm::uvec2& sceneSi
_sceneFbo->Bound([&] {
auto size = _sceneFbo->size;
Context::Viewport(size.x, size.y);
glBindTexture(GL_TEXTURE_2D, finalTexture);
glBindTexture(GL_TEXTURE_2D, _currentSceneTexture);
GLenum err = glGetError();
drawUnitQuad();
});
ovr_for_each_eye([&](ovrEyeType eye) {
_sceneLayer.RenderPose[eye] = _eyePoses[eye];
});
uint32_t frameIndex { 0 };
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;
viewScaleDesc.HmdSpaceToWorldScaleInMeters = 1.0f;
@ -228,19 +241,26 @@ void OculusDisplayPlugin::display(GLuint finalTexture, const glm::uvec2& sceneSi
}
}
_sceneFbo->Increment();
++_frameIndex;
#endif
}
/*
/*
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
*/
void OculusDisplayPlugin::finishFrame() {
*/
if (_enablePreview) {
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 {
public:
virtual void activate() override;
virtual void deactivate() override;
virtual const QString & getName() const override;
virtual void setEyeRenderPose(uint32_t frameIndex, Eye eye, const glm::mat4& pose) override final;
protected:
virtual void display(GLuint finalTexture, const glm::uvec2& sceneSize) override;
virtual void internalPresent() override;
virtual void customizeContext() override;
// Do not perform swap in finish
virtual void finishFrame() override;
virtual void uncustomizeContext() override;
private:
using EyePoses = std::pair<ovrPosef, ovrPosef>;
static const QString NAME;
bool _enablePreview { false };
bool _monoPreview { true };
QMap<uint32_t, EyePoses> _frameEyePoses;
#if (OVR_MAJOR_VERSION >= 6)
SwapFboPtr _sceneFbo;
#endif
};

View file

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

View file

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

View file

@ -91,7 +91,12 @@ public:
virtual void unsetFullscreen(const QScreen* avoidScreen = nullptr) override {}
virtual void showDisplayPluginsTools() 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 QGLWidget* getPrimaryWidget() override { return nullptr; }
virtual QWindow* getPrimaryWindow() override { return nullptr; }
virtual QOpenGLContext* getPrimaryContext() override { return nullptr; }
virtual bool isForeground() override { return true; }
virtual const DisplayPlugin* getActiveDisplayPlugin() const override { return nullptr; }
};