From 270f2f7a3b3e391056a5cc049a2acc2dd82f9205 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Tue, 25 Aug 2015 00:55:00 -0700 Subject: [PATCH] Fixing framerate limit, moving vsync to 2d plugin --- interface/src/Application.cpp | 227 +++++------------- interface/src/Application.h | 6 - interface/src/Menu.cpp | 21 +- interface/src/Menu.h | 7 - .../Basic2DWindowOpenGLDisplayPlugin.cpp | 109 ++++++++- .../Basic2DWindowOpenGLDisplayPlugin.h | 8 +- .../src/display-plugins/DisplayPlugin.cpp | 5 +- .../src/display-plugins/DisplayPlugin.h | 2 +- .../display-plugins/OpenGLDisplayPlugin.cpp | 27 +++ .../src/display-plugins/OpenGLDisplayPlugin.h | 4 + .../stereo/StereoDisplayPlugin.cpp | 2 +- libraries/shared/src/NumericalConstants.h | 1 + 12 files changed, 206 insertions(+), 213 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index fbe47eb83f..2ed91037be 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -189,6 +189,11 @@ static QTimer* billboardPacketTimer = NULL; static QTimer* checkFPStimer = NULL; static QTimer* idleTimer = NULL; +static const unsigned int TARGET_SIM_FRAMERATE = 60; +static const unsigned int THROTTLED_SIM_FRAMERATE = 15; +static const int TARGET_SIM_FRAME_PERIOD_MS = MSECS_PER_SECOND / TARGET_SIM_FRAMERATE; +static const int THROTTLED_SIM_FRAME_PERIOD_MS = MSECS_PER_SECOND / THROTTLED_SIM_FRAMERATE; + const QString CHECK_VERSION_URL = "https://highfidelity.com/latestVersion.xml"; const QString SKIP_FILENAME = QStandardPaths::writableLocation(QStandardPaths::DataLocation) + "/hifi.skipversion"; @@ -347,7 +352,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : _trayIcon(new QSystemTrayIcon(_window)), _lastNackTime(usecTimestampNow()), _lastSendDownstreamAudioStats(usecTimestampNow()), - _isVSyncOn(true), _isThrottleFPSEnabled(true), _aboutToQuit(false), _notifiedPacketVersionMismatchThisDomain(false), @@ -745,8 +749,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : }); connect(this, &Application::applicationStateChanged, this, &Application::activeChanged); - - setVSyncEnabled(); // make sure VSync is set properly at startup } void Application::aboutToQuit() { @@ -920,7 +922,7 @@ void Application::initializeGL() { // call our idle function whenever we can idleTimer = new QTimer(this); connect(idleTimer, SIGNAL(timeout()), SLOT(idle())); - idleTimer->start(0); + idleTimer->start(TARGET_SIM_FRAME_PERIOD_MS); _idleLoopStdev.reset(); if (_justStarted) { @@ -2033,38 +2035,38 @@ void Application::checkFPS() { } void Application::idle() { - PROFILE_RANGE(__FUNCTION__); - static SimpleAverage interIdleDurations; - - static uint64_t lastIdleStart{ 0 }; - static uint64_t lastIdleEnd{ 0 }; - uint64_t now = usecTimestampNow(); - uint64_t idleStartToStartDuration = now - lastIdleStart; - - if (lastIdleStart > 0 && idleStartToStartDuration > 0) { - _simsPerSecond.updateAverage((float)USECS_PER_SECOND / (float)idleStartToStartDuration); - } - - lastIdleStart = now; - - if (lastIdleEnd != 0) { - interIdleDurations.update(now - lastIdleEnd); - static uint64_t lastReportTime = now; - if ((now - lastReportTime) >= (USECS_PER_SECOND)) { - static QString LOGLINE("Average inter-idle time: %1 us for %2 samples"); - if (Menu::getInstance()->isOptionChecked(MenuOption::LogExtraTimings)) { - qCDebug(interfaceapp_timing) << LOGLINE.arg((int)interIdleDurations.getAverage()).arg(interIdleDurations.getCount()); - } - interIdleDurations.reset(); - lastReportTime = now; - } - } - - PerformanceTimer perfTimer("idle"); if (_aboutToQuit) { return; // bail early, nothing to do here. } + // depending on whether we're throttling or not. + // Once rendering is off on another thread we should be able to have Application::idle run at start(0) in + // perpetuity and not expect events to get backed up. + bool isThrottled = getActiveDisplayPlugin()->isThrottled(); + // Only run simulation code if more than the targetFramePeriod have passed since last time we ran + // This attempts to lock the simulation at 60 updates per second, regardless of framerate + float timeSinceLastUpdateUs = (float)_lastTimeUpdated.nsecsElapsed() / NSECS_PER_USEC; + float secondsSinceLastUpdate = timeSinceLastUpdateUs / USECS_PER_SECOND; + + if (isThrottled && (timeSinceLastUpdateUs / USECS_PER_MSEC) < THROTTLED_SIM_FRAME_PERIOD_MS) { + return; // bail early, we're throttled and not enough time has elapsed + } + + _lastTimeUpdated.start(); + + + { + PROFILE_RANGE(__FUNCTION__); + uint64_t now = usecTimestampNow(); + static uint64_t lastIdleStart{ now }; + uint64_t idleStartToStartDuration = now - lastIdleStart; + if (idleStartToStartDuration != 0) { + _simsPerSecond.updateAverage((float)USECS_PER_SECOND / (float)idleStartToStartDuration); + } + lastIdleStart = now; + } + + PerformanceTimer perfTimer("idle"); // Drop focus from _keyboardFocusedItem if no keyboard messages for 30 seconds if (!_keyboardFocusedItem.isInvalidID()) { const quint64 LOSE_FOCUS_AFTER_ELAPSED_TIME = 30 * USECS_PER_SECOND; // if idle for 30 seconds, drop focus @@ -2080,69 +2082,42 @@ void Application::idle() { bool showWarnings = getLogger()->extraDebugging(); PerformanceWarning warn(showWarnings, "idle()"); - // Only run simulation code if more than the targetFramePeriod have passed since last time we ran - double targetFramePeriod = 0.0; - unsigned int targetFramerate = getRenderTargetFramerate(); - if (targetFramerate > 0) { - targetFramePeriod = 1000.0 / targetFramerate; + { + PerformanceTimer perfTimer("update"); + PerformanceWarning warn(showWarnings, "Application::idle()... update()"); + static const float BIGGEST_DELTA_TIME_SECS = 0.25f; + update(glm::clamp(secondsSinceLastUpdate, 0.0f, BIGGEST_DELTA_TIME_SECS)); } - double timeSinceLastUpdate = (double)_lastTimeUpdated.nsecsElapsed() / 1000000.0; - if (timeSinceLastUpdate > targetFramePeriod) { - _lastTimeUpdated.start(); - { - PerformanceTimer perfTimer("update"); - PerformanceWarning warn(showWarnings, "Application::idle()... update()"); - const float BIGGEST_DELTA_TIME_SECS = 0.25f; - PROFILE_RANGE(__FUNCTION__ "/idleUpdate"); - update(glm::clamp((float)timeSinceLastUpdate / 1000.0f, 0.0f, BIGGEST_DELTA_TIME_SECS)); - } - { - PerformanceTimer perfTimer("updateGL"); - PerformanceWarning warn(showWarnings, "Application::idle()... updateGL()"); - getActiveDisplayPlugin()->idle(); - auto inputPlugins = PluginManager::getInstance()->getInputPlugins(); - foreach(auto inputPlugin, inputPlugins) { - QString name = inputPlugin->getName(); - QAction* action = Menu::getInstance()->getActionForOption(name); - if (action && action->isChecked()) { - inputPlugin->idle(); - } + { + PerformanceTimer perfTimer("pluginIdle"); + PerformanceWarning warn(showWarnings, "Application::idle()... pluginIdle()"); + getActiveDisplayPlugin()->idle(); + auto inputPlugins = PluginManager::getInstance()->getInputPlugins(); + foreach(auto inputPlugin, inputPlugins) { + QString name = inputPlugin->getName(); + QAction* action = Menu::getInstance()->getActionForOption(name); + if (action && action->isChecked()) { + inputPlugin->idle(); } } - { - PerformanceTimer perfTimer("rest"); - PerformanceWarning warn(showWarnings, "Application::idle()... rest of it"); - _idleLoopStdev.addValue(timeSinceLastUpdate); + } + { + PerformanceTimer perfTimer("rest"); + PerformanceWarning warn(showWarnings, "Application::idle()... rest of it"); + _idleLoopStdev.addValue(secondsSinceLastUpdate); - // Record standard deviation and reset counter if needed - const int STDEV_SAMPLES = 500; - if (_idleLoopStdev.getSamples() > STDEV_SAMPLES) { - _idleLoopMeasuredJitter = _idleLoopStdev.getStDev(); - _idleLoopStdev.reset(); - } - } - - float secondsSinceLastUpdate = (float)timeSinceLastUpdate / 1000.0f; - _overlayConductor.update(secondsSinceLastUpdate); - - // depending on whether we're throttling or not. - // Once rendering is off on another thread we should be able to have Application::idle run at start(0) in - // perpetuity and not expect events to get backed up. - - bool isThrottled = getActiveDisplayPlugin()->isThrottled(); - static const int THROTTLED_IDLE_TIMER_DELAY = MSECS_PER_SECOND / 15; - static const int IDLE_TIMER_DELAY_MS = 2; - int desiredInterval = isThrottled ? THROTTLED_IDLE_TIMER_DELAY : IDLE_TIMER_DELAY_MS; - //qDebug() << "isThrottled:" << isThrottled << "desiredInterval:" << desiredInterval; - - if (idleTimer->interval() != desiredInterval) { - idleTimer->start(desiredInterval); + // Record standard deviation and reset counter if needed + const int STDEV_SAMPLES = 500; + if (_idleLoopStdev.getSamples() > STDEV_SAMPLES) { + _idleLoopMeasuredJitter = _idleLoopStdev.getStDev(); + _idleLoopStdev.reset(); } } + + _overlayConductor.update(secondsSinceLastUpdate); // check for any requested background downloads. emit checkBackgroundDownloads(); - lastIdleEnd = usecTimestampNow(); } float Application::getAverageSimsPerSecond() { @@ -4476,90 +4451,10 @@ void Application::takeSnapshot() { } -void Application::setVSyncEnabled() { - _glWidget->makeCurrent(); -#if defined(Q_OS_WIN) - bool vsyncOn = Menu::getInstance()->isOptionChecked(MenuOption::RenderTargetFramerateVSyncOn); - if (wglewGetExtension("WGL_EXT_swap_control")) { - wglSwapIntervalEXT(vsyncOn); - int swapInterval = wglGetSwapIntervalEXT(); - qCDebug(interfaceapp, "V-Sync is %s\n", (swapInterval > 0 ? "ON" : "OFF")); - } else { - qCDebug(interfaceapp, "V-Sync is FORCED ON on this system\n"); - } -#elif defined(Q_OS_LINUX) - // TODO: write the poper code for linux - /* - if (glQueryExtension.... ("GLX_EXT_swap_control")) { - glxSwapIntervalEXT(vsyncOn); - int swapInterval = xglGetSwapIntervalEXT(); - _isVSyncOn = swapInterval; - qCDebug(interfaceapp, "V-Sync is %s\n", (swapInterval > 0 ? "ON" : "OFF")); - } else { - qCDebug(interfaceapp, "V-Sync is FORCED ON on this system\n"); - } - */ -#else - qCDebug(interfaceapp, "V-Sync is FORCED ON on this system\n"); -#endif - _offscreenContext->makeCurrent(); -} - void Application::setThrottleFPSEnabled() { _isThrottleFPSEnabled = Menu::getInstance()->isOptionChecked(MenuOption::ThrottleFPSIfNotFocus); } -bool Application::isVSyncOn() const { -#if defined(Q_OS_WIN) - if (wglewGetExtension("WGL_EXT_swap_control")) { - int swapInterval = wglGetSwapIntervalEXT(); - return (swapInterval > 0); - } -#elif defined(Q_OS_LINUX) - // TODO: write the poper code for linux - /* - if (glQueryExtension.... ("GLX_EXT_swap_control")) { - int swapInterval = xglGetSwapIntervalEXT(); - return (swapInterval > 0); - } else { - return true; - } - */ -#endif - return true; -} - -bool Application::isVSyncEditable() const { -#if defined(Q_OS_WIN) - if (wglewGetExtension("WGL_EXT_swap_control")) { - return true; - } -#elif defined(Q_OS_LINUX) - // TODO: write the poper code for linux - /* - if (glQueryExtension.... ("GLX_EXT_swap_control")) { - return true; - } - */ -#endif - return false; -} - -unsigned int Application::getRenderTargetFramerate() const { - if (Menu::getInstance()->isOptionChecked(MenuOption::RenderTargetFramerateUnlimited)) { - return 0; - } else if (Menu::getInstance()->isOptionChecked(MenuOption::RenderTargetFramerate60)) { - return 60; - } else if (Menu::getInstance()->isOptionChecked(MenuOption::RenderTargetFramerate50)) { - return 50; - } else if (Menu::getInstance()->isOptionChecked(MenuOption::RenderTargetFramerate40)) { - return 40; - } else if (Menu::getInstance()->isOptionChecked(MenuOption::RenderTargetFramerate30)) { - return 30; - } - return 0; -} - float Application::getRenderResolutionScale() const { if (Menu::getInstance()->isOptionChecked(MenuOption::RenderResolutionOne)) { return 1.0f; diff --git a/interface/src/Application.h b/interface/src/Application.h index 6394aa12d0..7e4e340eee 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -300,9 +300,6 @@ public: float getRenderResolutionScale() const; int getRenderAmbientLight() const; - unsigned int getRenderTargetFramerate() const; - bool isVSyncOn() const; - bool isVSyncEditable() const; bool isAboutToQuit() const { return _aboutToQuit; } // the isHMDmode is true whenever we use the interface from an HMD and not a standard flat display @@ -411,8 +408,6 @@ public slots: void domainSettingsReceived(const QJsonObject& domainSettingsObject); - void setVSyncEnabled(); - void setThrottleFPSEnabled(); bool isThrottleFPSEnabled() { return _isThrottleFPSEnabled; } @@ -626,7 +621,6 @@ private: quint64 _lastNackTime; quint64 _lastSendDownstreamAudioStats; - bool _isVSyncOn; bool _isThrottleFPSEnabled; bool _aboutToQuit; diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 9d47df1c73..b756236fe0 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -222,7 +222,7 @@ Menu::Menu() { addActionToQMenuAndActionHash(toolsMenu, MenuOption::PackageModel, 0, qApp, SLOT(packageModel())); - MenuWrapper* displayMenu = addMenu(DisplayPlugin::MENU_PATH); + MenuWrapper* displayMenu = addMenu(DisplayPlugin::MENU_PATH()); { MenuWrapper* displayModeMenu = addMenu(MenuOption::OutputMenu); QActionGroup* displayModeGroup = new QActionGroup(displayModeMenu); @@ -333,25 +333,8 @@ Menu::Menu() { ambientLightGroup->addAction(addCheckableActionToQMenuAndActionHash(ambientLightMenu, MenuOption::RenderAmbientLight8, 0, false)); ambientLightGroup->addAction(addCheckableActionToQMenuAndActionHash(ambientLightMenu, MenuOption::RenderAmbientLight9, 0, false)); - { - MenuWrapper* framerateMenu = renderOptionsMenu->addMenu(MenuOption::RenderTargetFramerate); - QActionGroup* framerateGroup = new QActionGroup(framerateMenu); - framerateGroup->setExclusive(true); - framerateGroup->addAction(addCheckableActionToQMenuAndActionHash(framerateMenu, MenuOption::RenderTargetFramerateUnlimited, 0, true)); - framerateGroup->addAction(addCheckableActionToQMenuAndActionHash(framerateMenu, MenuOption::RenderTargetFramerate60, 0, false)); - framerateGroup->addAction(addCheckableActionToQMenuAndActionHash(framerateMenu, MenuOption::RenderTargetFramerate50, 0, false)); - framerateGroup->addAction(addCheckableActionToQMenuAndActionHash(framerateMenu, MenuOption::RenderTargetFramerate40, 0, false)); - framerateGroup->addAction(addCheckableActionToQMenuAndActionHash(framerateMenu, MenuOption::RenderTargetFramerate30, 0, false)); - -#if defined(Q_OS_MAC) -#else - addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::RenderTargetFramerateVSyncOn, 0, true, - qApp, SLOT(setVSyncEnabled())); -#endif - addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::ThrottleFPSIfNotFocus, 0, true, + addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::ThrottleFPSIfNotFocus, 0, true, qApp, SLOT(setThrottleFPSEnabled())); - } - MenuWrapper* resolutionMenu = renderOptionsMenu->addMenu(MenuOption::RenderResolution); QActionGroup* resolutionGroup = new QActionGroup(resolutionMenu); diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 4bd1e7f664..98b3fe9892 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -238,13 +238,6 @@ namespace MenuOption { const QString RenderLookAtTargets = "Show Look-at Targets"; const QString RenderLookAtVectors = "Show Look-at Vectors"; const QString RenderSkeletonCollisionShapes = "Show Skeleton Collision Shapes"; - const QString RenderTargetFramerate = "Framerate"; - const QString RenderTargetFramerateUnlimited = "Unlimited"; - const QString RenderTargetFramerate60 = "60"; - const QString RenderTargetFramerate50 = "50"; - const QString RenderTargetFramerate40 = "40"; - const QString RenderTargetFramerate30 = "30"; - const QString RenderTargetFramerateVSyncOn = "V-Sync On"; const QString RenderResolution = "Scale Resolution"; const QString RenderResolutionOne = "1"; const QString RenderResolutionTwoThird = "2/3"; diff --git a/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.cpp index da8deefa74..914d30d58a 100644 --- a/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.cpp @@ -7,21 +7,35 @@ // #include "Basic2DWindowOpenGLDisplayPlugin.h" +#include + +#include +#include +#include + #include -#include -#include const QString Basic2DWindowOpenGLDisplayPlugin::NAME("2D Display"); static const QString FULLSCREEN = "Fullscreen"; +static const QString FRAMERATE = DisplayPlugin::MENU_PATH() + ">Framerate"; +static const QString FRAMERATE_UNLIMITED = "Unlimited"; +static const QString FRAMERATE_60 = "60"; +static const QString FRAMERATE_50 = "50"; +static const QString FRAMERATE_40 = "40"; +static const QString FRAMERATE_30 = "30"; +static const QString VSYNC_ON = "V-Sync On"; const QString& Basic2DWindowOpenGLDisplayPlugin::getName() const { return NAME; } +std::vector _framerateActions; +QAction* _vsyncAction{ nullptr }; + void Basic2DWindowOpenGLDisplayPlugin::activate() { - CONTAINER->addMenu(MENU_PATH); - CONTAINER->addMenuItem(MENU_PATH, FULLSCREEN, + _framerateActions.clear(); + CONTAINER->addMenuItem(MENU_PATH(), FULLSCREEN, [this](bool clicked) { if (clicked) { CONTAINER->setFullscreen(getFullscreenTarget()); @@ -29,18 +43,65 @@ void Basic2DWindowOpenGLDisplayPlugin::activate() { 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; + } + + updateFramerate(); } void Basic2DWindowOpenGLDisplayPlugin::deactivate() { WindowOpenGLDisplayPlugin::deactivate(); } -int Basic2DWindowOpenGLDisplayPlugin::getDesiredInterval(bool isThrottled) const { - static const int THROTTLED_PAINT_TIMER_DELAY = MSECS_PER_SECOND / 15; - static const int PAINT_TIMER_DELAY_MS = 1; +void Basic2DWindowOpenGLDisplayPlugin::display(GLuint sceneTexture, const glm::uvec2& sceneSize) { + if (_vsyncAction) { + bool wantVsync = _vsyncAction->isChecked(); + bool vsyncEnabed = isVsyncEnabled(); + if (vsyncEnabed ^ wantVsync) { + enableVsync(wantVsync); + } + } - return isThrottled ? THROTTLED_PAINT_TIMER_DELAY : PAINT_TIMER_DELAY_MS; + WindowOpenGLDisplayPlugin::display(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; + int result = ULIMIITED_PAINT_TIMER_DELAY_MS; + if (_isThrottled) { + result = THROTTLED_PAINT_TIMER_DELAY_MS; + } + if (0 != _framerateTarget) { + result = MSECS_PER_SECOND / _framerateTarget; + } + + qDebug() << "New interval " << result; + return result; } bool Basic2DWindowOpenGLDisplayPlugin::isThrottled() const { @@ -49,14 +110,42 @@ bool Basic2DWindowOpenGLDisplayPlugin::isThrottled() const { bool shouldThrottle = (!CONTAINER->isForeground() && CONTAINER->isOptionChecked(ThrottleFPSIfNotFocus)); if (_isThrottled != shouldThrottle) { - int desiredInterval = getDesiredInterval(shouldThrottle); - _timer.start(desiredInterval); _isThrottled = shouldThrottle; + _timer.start(getDesiredInterval()); } return shouldThrottle; } + +void Basic2DWindowOpenGLDisplayPlugin::updateFramerate() { + QAction* checkedFramerate{ nullptr }; + foreach(auto action, _framerateActions) { + if (action->isChecked()) { + checkedFramerate = action; + break; + } + } + + _framerateTarget = 0; + if (checkedFramerate) { + QString actionText = checkedFramerate->text(); + if (FRAMERATE_60 == actionText) { + _framerateTarget = 60; + } else if (FRAMERATE_50 == actionText) { + _framerateTarget = 50; + } else if (FRAMERATE_40 == actionText) { + _framerateTarget = 40; + } else if (FRAMERATE_30 == actionText) { + _framerateTarget = 30; + } + } + + int newInterval = getDesiredInterval(); + qDebug() << newInterval; + _timer.start(getDesiredInterval()); +} + // FIXME target the screen the window is currently on QScreen* Basic2DWindowOpenGLDisplayPlugin::getFullscreenTarget() { return qApp->primaryScreen(); diff --git a/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.h index 64edfe3600..f4655ab79f 100644 --- a/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.h @@ -14,19 +14,23 @@ class Basic2DWindowOpenGLDisplayPlugin : public WindowOpenGLDisplayPlugin { Q_OBJECT public: + virtual const QString & getName() const override; + virtual void activate() override; virtual void deactivate() override; - virtual const QString & getName() const override; + virtual void display(GLuint sceneTexture, const glm::uvec2& sceneSize) override; virtual bool isThrottled() const override; protected: - int getDesiredInterval(bool isThrottled) const; + int getDesiredInterval() const; mutable bool _isThrottled = false; private: + void updateFramerate(); static const QString NAME; QScreen* getFullscreenTarget(); + uint32_t _framerateTarget{ 0 }; int _fullscreenTarget{ -1 }; }; diff --git a/libraries/display-plugins/src/display-plugins/DisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/DisplayPlugin.cpp index 5840b3cbba..598e78e500 100644 --- a/libraries/display-plugins/src/display-plugins/DisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/DisplayPlugin.cpp @@ -18,7 +18,10 @@ #include "oculus/OculusDisplayPlugin.h" #include "oculus/OculusLegacyDisplayPlugin.h" -const QString DisplayPlugin::MENU_PATH{ "Display" }; +const QString& DisplayPlugin::MENU_PATH() { + static const QString value = "Display"; + return value; +} // TODO migrate to a DLL model where plugins are discovered and loaded at runtime by the PluginManager class DisplayPluginList getDisplayPlugins() { diff --git a/libraries/display-plugins/src/display-plugins/DisplayPlugin.h b/libraries/display-plugins/src/display-plugins/DisplayPlugin.h index 0696cc6229..a9220d68f6 100644 --- a/libraries/display-plugins/src/display-plugins/DisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/DisplayPlugin.h @@ -120,7 +120,7 @@ public: virtual void resetSensors() {} virtual float devicePixelRatio() { return 1.0; } - static const QString MENU_PATH; + static const QString& MENU_PATH(); signals: void recommendedFramebufferSizeChanged(const QSize & size); void requestRender(); diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp index 0409899739..eb38e1bf4f 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp @@ -38,6 +38,11 @@ void OpenGLDisplayPlugin::finishFrame() { 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); @@ -46,6 +51,8 @@ void OpenGLDisplayPlugin::customizeContext() { _program = loadDefaultShader(); _plane = loadPlane(_program); + + enableVsync(); } void OpenGLDisplayPlugin::activate() { @@ -114,4 +121,24 @@ void OpenGLDisplayPlugin::display( void OpenGLDisplayPlugin::drawUnitQuad() { _program->Bind(); _plane->Draw(); +} + +void OpenGLDisplayPlugin::enableVsync(bool enable) { + if (!_vsyncSupported) { + return; + } +#ifdef Q_OS_WIN + wglSwapIntervalEXT(enable ? 1 : 0); +#endif +} + +bool OpenGLDisplayPlugin::isVsyncEnabled() { + if (!_vsyncSupported) { + return true; + } +#ifdef Q_OS_WIN + return wglGetSwapIntervalEXT() != 0; +#else + return true; +#endif } \ No newline at end of file diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h index 3152500232..0dc94b72f5 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h @@ -38,9 +38,13 @@ protected: virtual void doneCurrent() = 0; virtual void swapBuffers() = 0; + virtual bool isVsyncEnabled(); + virtual void enableVsync(bool enable = true); + mutable QTimer _timer; ProgramPtr _program; ShapeWrapperPtr _plane; + bool _vsyncSupported{ false }; }; diff --git a/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.cpp index df691f06f3..017977bf69 100644 --- a/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.cpp @@ -69,7 +69,7 @@ void StereoDisplayPlugin::activate() { if (screen == qApp->primaryScreen()) { checked = true; } - auto action = CONTAINER->addMenuItem(MENU_PATH, name, + auto action = CONTAINER->addMenuItem(MENU_PATH(), name, [this](bool clicked) { updateScreen(); }, true, checked, "Screens"); _screenActions[i] = action; } diff --git a/libraries/shared/src/NumericalConstants.h b/libraries/shared/src/NumericalConstants.h index b632147641..9a946e35f7 100644 --- a/libraries/shared/src/NumericalConstants.h +++ b/libraries/shared/src/NumericalConstants.h @@ -31,6 +31,7 @@ const float METERS_PER_DECIMETER = 0.1f; const float METERS_PER_CENTIMETER = 0.01f; const float METERS_PER_MILLIMETER = 0.001f; const float MILLIMETERS_PER_METER = 1000.0f; +const quint64 NSECS_PER_USEC = 1000; const quint64 USECS_PER_MSEC = 1000; const quint64 MSECS_PER_SECOND = 1000; const quint64 USECS_PER_SECOND = USECS_PER_MSEC * MSECS_PER_SECOND;