1
0
Fork 1
mirror of https://github.com/TES3MP/openmw-tes3mp.git synced 2025-02-06 09:45:32 +00:00

Fix frame rate limit

Measure time at the computation end but before sleep. This allows to adjust
sleep interval for the next frame in case sleep is not precise due to syscall
overhead or too low timer resolution.

Remove old frame limiting mechanism.
This commit is contained in:
elsid 2021-02-11 18:19:55 +01:00
parent 5566f2b06b
commit 8ab5fd9b40
No known key found for this signature in database
GPG key ID: D27B8E8D10A2896B
5 changed files with 72 additions and 29 deletions

View file

@ -38,6 +38,8 @@
#include <components/detournavigator/navigator.hpp>
#include <components/misc/frameratelimiter.hpp>
#include "mwinput/inputmanagerimp.hpp"
#include "mwgui/windowmanagerimp.hpp"
@ -918,13 +920,15 @@ void OMW::Engine::go()
}
// Start the main rendering loop
osg::Timer frameTimer;
double simulationTime = 0.0;
Misc::FrameRateLimiter frameRateLimiter = Misc::makeFrameRateLimiter(mEnvironment.getFrameRateLimit());
const std::chrono::steady_clock::duration maxSimulationInterval(std::chrono::milliseconds(200));
while (!mViewer->done() && !mEnvironment.getStateManager()->hasQuitRequest())
{
double dt = frameTimer.time_s();
frameTimer.setStartTick();
dt = std::min(dt, 0.2);
const double dt = std::chrono::duration_cast<std::chrono::duration<double>>(std::min(
frameRateLimiter.getLastFrameDuration(),
maxSimulationInterval
)).count();
mViewer->advance(simulationTime);
@ -960,7 +964,7 @@ void OMW::Engine::go()
}
}
mEnvironment.limitFrameRate(frameTimer.time_s());
frameRateLimiter.limit();
}
// Save user settings

View file

@ -1,8 +1,6 @@
#include "environment.hpp"
#include <cassert>
#include <chrono>
#include <thread>
#include <components/resource/resourcesystem.hpp>
@ -98,19 +96,6 @@ 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<double>(mFrameRateLimit);
if (thisFrameTime < minFrameTime)
{
std::this_thread::sleep_for(std::chrono::duration<double>(minFrameTime - thisFrameTime));
}
}
}
MWBase::World *MWBase::Environment::getWorld() const
{
assert (mWorld);

View file

@ -83,7 +83,6 @@ namespace MWBase
void setFrameRateLimit(float frameRateLimit);
float getFrameRateLimit() const;
void limitFrameRate(double dt) const;
World *getWorld() const;

View file

@ -50,6 +50,7 @@
#include <components/widgets/tags.hpp>
#include <components/misc/resourcehelpers.hpp>
#include <components/misc/frameratelimiter.hpp>
#include "../mwbase/inputmanager.hpp"
#include "../mwbase/statemanager.hpp"
@ -710,12 +711,11 @@ namespace MWGui
if (block)
{
osg::Timer frameTimer;
Misc::FrameRateLimiter frameRateLimiter = Misc::makeFrameRateLimiter(MWBase::Environment::get().getFrameRateLimit());
while (mMessageBoxManager->readPressedButton(false) == -1
&& !MWBase::Environment::get().getStateManager()->hasQuitRequest())
{
double dt = frameTimer.time_s();
frameTimer.setStartTick();
const double dt = std::chrono::duration_cast<std::chrono::duration<double>>(frameRateLimiter.getLastFrameDuration()).count();
mKeyboardNavigation->onFrame();
mMessageBoxManager->onFrame(dt);
@ -734,7 +734,7 @@ namespace MWGui
// refer to the advance() and frame() order in Engine::go()
mViewer->advance(mViewer->getFrameStamp()->getSimulationTime());
MWBase::Environment::get().limitFrameRate(frameTimer.time_s());
frameRateLimiter.limit();
}
}
}
@ -1750,11 +1750,10 @@ namespace MWGui
~MWSound::Type::Movie & MWSound::Type::Mask
);
osg::Timer frameTimer;
Misc::FrameRateLimiter frameRateLimiter = Misc::makeFrameRateLimiter(MWBase::Environment::get().getFrameRateLimit());
while (mVideoWidget->update() && !MWBase::Environment::get().getStateManager()->hasQuitRequest())
{
double dt = frameTimer.time_s();
frameTimer.setStartTick();
const double dt = std::chrono::duration_cast<std::chrono::duration<double>>(frameRateLimiter.getLastFrameDuration()).count();
MWBase::Environment::get().getInputManager()->update(dt, true, false);
@ -1777,7 +1776,7 @@ namespace MWGui
// refer to the advance() and frame() order in Engine::go()
mViewer->advance(mViewer->getFrameStamp()->getSimulationTime());
MWBase::Environment::get().limitFrameRate(frameTimer.time_s());
frameRateLimiter.limit();
}
mVideoWidget->stop();

View file

@ -0,0 +1,56 @@
#ifndef OPENMW_COMPONENTS_MISC_FRAMERATELIMITER_H
#define OPENMW_COMPONENTS_MISC_FRAMERATELIMITER_H
#include <chrono>
#include <thread>
namespace Misc
{
class FrameRateLimiter
{
public:
template <class Rep, class Ratio>
explicit FrameRateLimiter(std::chrono::duration<Rep, Ratio> maxFrameDuration,
std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now())
: mMaxFrameDuration(std::chrono::duration_cast<std::chrono::steady_clock::duration>(maxFrameDuration))
, mLastMeasurement(now)
{}
std::chrono::steady_clock::duration getLastFrameDuration() const
{
return mLastFrameDuration;
}
void limit(std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now())
{
const auto passed = now - mLastMeasurement;
const auto left = mMaxFrameDuration - passed;
if (left > left.zero())
{
std::this_thread::sleep_for(left);
mLastMeasurement = now + left;
mLastFrameDuration = mMaxFrameDuration;
}
else
{
mLastMeasurement = now;
mLastFrameDuration = passed;
}
}
private:
std::chrono::steady_clock::duration mMaxFrameDuration;
std::chrono::steady_clock::time_point mLastMeasurement;
std::chrono::steady_clock::duration mLastFrameDuration;
};
inline Misc::FrameRateLimiter makeFrameRateLimiter(float frameRateLimit)
{
if (frameRateLimit > 0.0f)
return Misc::FrameRateLimiter(std::chrono::duration<float>(1.0f / frameRateLimit));
else
return Misc::FrameRateLimiter(std::chrono::steady_clock::duration::zero());
}
}
#endif