From 8c6a8ca48d7077e360ff49cc2795cad271b2d139 Mon Sep 17 00:00:00 2001 From: scrawl <720642+scrawl@users.noreply.github.com> Date: Wed, 30 Aug 2017 21:26:30 +0000 Subject: [PATCH] Respect the framelimit in all cases (Fixes #3531) Affects loading screen, videos & modal dialogs. Also skips rendering if window is minimized. --- apps/openmw/engine.cpp | 16 ++++------ apps/openmw/mwbase/environment.cpp | 27 ++++++++++++++++- apps/openmw/mwbase/environment.hpp | 5 ++++ apps/openmw/mwgui/loadingscreen.cpp | 15 ++++++++-- apps/openmw/mwgui/loadingscreen.hpp | 2 ++ apps/openmw/mwgui/windowmanagerimp.cpp | 41 +++++++++++++++++++------- 6 files changed, 81 insertions(+), 25 deletions(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index c921e17d0..f5ec86cc3 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -84,7 +84,6 @@ void OMW::Engine::frame(float frametime) try { mStartTick = mViewer->getStartTick(); - mEnvironment.setFrameDuration (frametime); // update input mEnvironment.getInputManager()->update(frametime, false); @@ -651,6 +650,8 @@ void OMW::Engine::go() Settings::Manager::getString("screenshot format", "General"))); mViewer->addEventHandler(mScreenCaptureHandler); + mEnvironment.setFrameRateLimit(Settings::Manager::getFloat("framerate limit", "Video")); + // Create encoder ToUTF8::Utf8Encoder encoder (mEncoding); mEncoder = &encoder; @@ -684,7 +685,6 @@ void OMW::Engine::go() // Start the main rendering loop osg::Timer frameTimer; double simulationTime = 0.0; - float framerateLimit = Settings::Manager::getFloat("framerate limit", "Video"); while (!mViewer->done() && !mEnvironment.getStateManager()->hasQuitRequest()) { double dt = frameTimer.time_s(); @@ -697,6 +697,8 @@ void OMW::Engine::go() mViewer->advance(simulationTime); + mEnvironment.setFrameDuration(dt); + frame(dt); if (!mEnvironment.getInputManager()->isWindowVisible()) @@ -714,15 +716,7 @@ void OMW::Engine::go() mViewer->renderingTraversals(); } - if (framerateLimit > 0.f) - { - double thisFrameTime = frameTimer.time_s(); - double minFrameTime = 1.0 / framerateLimit; - if (thisFrameTime < minFrameTime) - { - OpenThreads::Thread::microSleep(1000*1000*(minFrameTime-thisFrameTime)); - } - } + mEnvironment.limitFrameRate(frameTimer.time_s()); } // Save user settings diff --git a/apps/openmw/mwbase/environment.cpp b/apps/openmw/mwbase/environment.cpp index 4efa7c273..5d01525b9 100644 --- a/apps/openmw/mwbase/environment.cpp +++ b/apps/openmw/mwbase/environment.cpp @@ -2,6 +2,8 @@ #include +#include + #include "world.hpp" #include "scriptmanager.hpp" #include "dialoguemanager.hpp" @@ -17,7 +19,7 @@ MWBase::Environment *MWBase::Environment::sThis = 0; MWBase::Environment::Environment() : mWorld (0), mSoundManager (0), mScriptManager (0), mWindowManager (0), mMechanicsManager (0), mDialogueManager (0), mJournal (0), mInputManager (0), mStateManager (0), - mFrameDuration (0) + mFrameDuration (0), mFrameRateLimit(0.f) { assert (!sThis); sThis = this; @@ -79,6 +81,29 @@ void MWBase::Environment::setFrameDuration (float duration) mFrameDuration = duration; } +void MWBase::Environment::setFrameRateLimit(float limit) +{ + mFrameRateLimit = limit; +} + +float MWBase::Environment::getFrameRateLimit() const +{ + return mFrameRateLimit; +} + +void MWBase::Environment::limitFrameRate(double dt) const +{ + if (mFrameRateLimit > 0.f) + { + double thisFrameTime = dt; + double minFrameTime = 1.0 / static_cast(mFrameRateLimit); + if (thisFrameTime < minFrameTime) + { + OpenThreads::Thread::microSleep(1000*1000*(minFrameTime-thisFrameTime)); + } + } +} + MWBase::World *MWBase::Environment::getWorld() const { assert (mWorld); diff --git a/apps/openmw/mwbase/environment.hpp b/apps/openmw/mwbase/environment.hpp index 7f7919f81..9163b21f3 100644 --- a/apps/openmw/mwbase/environment.hpp +++ b/apps/openmw/mwbase/environment.hpp @@ -33,6 +33,7 @@ namespace MWBase InputManager *mInputManager; StateManager *mStateManager; float mFrameDuration; + float mFrameRateLimit; Environment (const Environment&); ///< not implemented @@ -67,6 +68,10 @@ namespace MWBase void setFrameDuration (float duration); ///< Set length of current frame in seconds. + void setFrameRateLimit(float frameRateLimit); + float getFrameRateLimit() const; + void limitFrameRate(double dt) const; + World *getWorld() const; SoundManager *getSoundManager() const; diff --git a/apps/openmw/mwgui/loadingscreen.cpp b/apps/openmw/mwgui/loadingscreen.cpp index ca6a0b0a4..c5836b653 100644 --- a/apps/openmw/mwgui/loadingscreen.cpp +++ b/apps/openmw/mwgui/loadingscreen.cpp @@ -102,6 +102,15 @@ namespace MWGui mBackgroundImage->setVisible(visible); } + double LoadingScreen::getTargetFrameRate() const + { + double frameRateLimit = MWBase::Environment::get().getFrameRateLimit(); + if (frameRateLimit > 0) + return std::min(frameRateLimit, mTargetFrameRate); + else + return mTargetFrameRate; + } + class CopyFramebufferToTextureCallback : public osg::Camera::DrawCallback { public: @@ -141,7 +150,7 @@ namespace MWGui if (mViewer->getIncrementalCompileOperation()) { mViewer->getIncrementalCompileOperation()->setMaximumNumOfObjectsToCompilePerFrame(100); - mViewer->getIncrementalCompileOperation()->setTargetFrameRate(mTargetFrameRate); + mViewer->getIncrementalCompileOperation()->setTargetFrameRate(getTargetFrameRate()); } // Assign dummy bounding sphere callback to avoid the bounding sphere of the entire scene being recomputed after each frame of loading @@ -210,7 +219,7 @@ namespace MWGui void LoadingScreen::setProgress (size_t value) { // skip expensive update if there isn't enough visible progress - if (value - mProgress < mProgressBar->getScrollRange()/200.f) + if (mProgressBar->getWidth() <= 0 || value - mProgress < mProgressBar->getScrollRange()/mProgressBar->getWidth()) return; value = std::min(value, mProgressBar->getScrollRange()-1); mProgress = value; @@ -231,7 +240,7 @@ namespace MWGui bool LoadingScreen::needToDrawLoadingScreen() { - if ( mTimer.time_m() <= mLastRenderTime + (1.0/mTargetFrameRate) * 1000.0) + if ( mTimer.time_m() <= mLastRenderTime + (1.0/getTargetFrameRate()) * 1000.0) return false; // the minimal delay before a loading screen shows diff --git a/apps/openmw/mwgui/loadingscreen.hpp b/apps/openmw/mwgui/loadingscreen.hpp index 1a53495a9..2f8831fdc 100644 --- a/apps/openmw/mwgui/loadingscreen.hpp +++ b/apps/openmw/mwgui/loadingscreen.hpp @@ -43,6 +43,8 @@ namespace MWGui virtual void setVisible(bool visible); + double getTargetFrameRate() const; + private: void findSplashScreens(); bool needToDrawLoadingScreen(); diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index b0d0d8acd..33ba58cc7 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -906,19 +906,30 @@ namespace MWGui if (block) { + osg::Timer frameTimer; while (mMessageBoxManager->readPressedButton(false) == -1 && !MWBase::Environment::get().getStateManager()->hasQuitRequest()) { - mMessageBoxManager->onFrame(0.f); - MWBase::Environment::get().getInputManager()->update(0, true, false); + double dt = frameTimer.time_s(); + frameTimer.setStartTick(); + mMessageBoxManager->onFrame(dt); + MWBase::Environment::get().getInputManager()->update(dt, true, false); + + if (!MWBase::Environment::get().getInputManager()->isWindowVisible()) + OpenThreads::Thread::microSleep(5000); + else + { + mViewer->eventTraversal(); + mViewer->updateTraversal(); + mViewer->renderingTraversals(); + } // at the time this function is called we are in the middle of a frame, // so out of order calls are necessary to get a correct frameNumber for the next frame. // refer to the advance() and frame() order in Engine::go() - mViewer->eventTraversal(); - mViewer->updateTraversal(); - mViewer->renderingTraversals(); mViewer->advance(mViewer->getFrameStamp()->getSimulationTime()); + + MWBase::Environment::get().limitFrameRate(frameTimer.time_s()); } } } @@ -1838,18 +1849,28 @@ namespace MWGui if (mVideoWidget->hasAudioStream()) MWBase::Environment::get().getSoundManager()->pauseSounds( MWBase::SoundManager::Play_TypeMask&(~MWBase::SoundManager::Play_TypeMovie)); - + osg::Timer frameTimer; while (mVideoWidget->update() && !MWBase::Environment::get().getStateManager()->hasQuitRequest()) { - MWBase::Environment::get().getInputManager()->update(0, true, false); + double dt = frameTimer.time_s(); + frameTimer.setStartTick(); + MWBase::Environment::get().getInputManager()->update(dt, true, false); + + if (!MWBase::Environment::get().getInputManager()->isWindowVisible()) + OpenThreads::Thread::microSleep(5000); + else + { + mViewer->eventTraversal(); + mViewer->updateTraversal(); + mViewer->renderingTraversals(); + } // at the time this function is called we are in the middle of a frame, // so out of order calls are necessary to get a correct frameNumber for the next frame. // refer to the advance() and frame() order in Engine::go() - mViewer->eventTraversal(); - mViewer->updateTraversal(); - mViewer->renderingTraversals(); mViewer->advance(mViewer->getFrameStamp()->getSimulationTime()); + + MWBase::Environment::get().limitFrameRate(frameTimer.time_s()); } mVideoWidget->stop();