1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-12-24 22:14:36 +02:00

FramerateManager now uses chrono instead of SDL_Ticks

This commit is contained in:
Ivan Savenko 2023-05-13 01:12:11 +03:00
parent 03df274450
commit 3ecdff2a21
6 changed files with 65 additions and 68 deletions

View File

@ -16,7 +16,6 @@
#include "mainmenu/CMainMenu.h"
#include "mainmenu/CPrologEpilogVideo.h"
#include "gui/CursorHandler.h"
#include "gui/FramerateManager.h"
#include "CPlayerInterface.h"
#include "CVideoHandler.h"
#include "CMusicHandler.h"
@ -600,7 +599,6 @@ static void mainLoop()
fsChanged([](const JsonNode &newState){ CGuiHandler::pushUserEvent(EUserEvent::FULLSCREEN_TOGGLED); });
inGuiThread.reset(new bool(true));
GH.framerateManager().init(settings["video"]["targetfps"].Integer());
while(1) //main SDL events loop
{

View File

@ -717,11 +717,13 @@ CGuiHandler::~CGuiHandler()
ShortcutHandler & CGuiHandler::shortcutsHandler()
{
assert(shortcutsHandlerInstance);
return *shortcutsHandlerInstance;
}
FramerateManager & CGuiHandler::framerateManager()
{
assert(framerateManagerInstance);
return *framerateManagerInstance;
}

View File

@ -93,13 +93,19 @@ public:
public:
//objs to blit
std::vector<std::shared_ptr<IShowActivatable>> objsToBlit;
/// returns current position of mouse cursor, relative to vcmi window
const Point & getCursorPosition() const;
ShortcutHandler & shortcutsHandler();
FramerateManager & framerateManager();
/// returns duration of last frame in milliseconds
/// NOTE: avoid to use, preferred method is to overload CIntObject::tick(uint32_t)
uint32_t getFrameDeltaMilliseconds() const;
/// Returns current logical screen dimensions
/// May not match size of window if user has UI scaling different from 100%
Point screenDimensions() const;
/// returns true if at least one mouse button is pressed

View File

@ -11,55 +11,47 @@
#include "StdInc.h"
#include "FramerateManager.h"
#include <SDL_timer.h>
FramerateManager::FramerateManager(int newRate)
: rate(0)
, rateticks(0)
, fps(0)
, accumulatedFrames(0)
, accumulatedTime(0)
, lastticks(0)
, timeElapsed(0)
FramerateManager::FramerateManager(int targetFrameRate)
: targetFrameTime(Duration(boost::chrono::seconds(1)) / targetFrameRate)
, lastFrameIndex(0)
, lastFrameTimes({})
, lastTimePoint (Clock::now())
{
init(newRate);
}
void FramerateManager::init(int newRate)
{
rate = newRate;
rateticks = 1000.0 / rate;
this->lastticks = SDL_GetTicks();
boost::range::fill(lastFrameTimes, targetFrameTime);
}
void FramerateManager::framerateDelay()
{
ui32 currentTicks = SDL_GetTicks();
timeElapsed = currentTicks - lastticks;
accumulatedFrames++;
Duration timeSpentBusy = Clock::now() - lastTimePoint;
// FPS is higher than it should be, then wait some time
if(timeElapsed < rateticks)
{
int timeToSleep = (uint32_t)ceil(this->rateticks) - timeElapsed;
boost::this_thread::sleep(boost::posix_time::milliseconds(timeToSleep));
}
if(timeSpentBusy < targetFrameTime)
boost::this_thread::sleep_for(targetFrameTime - timeSpentBusy);
currentTicks = SDL_GetTicks();
// recalculate timeElapsed for external calls via getElapsed()
// compute actual timeElapsed taking into account actual sleep interval
// limit it to 100 ms to avoid breaking animation in case of huge lag (e.g. triggered breakpoint)
timeElapsed = std::min<ui32>(currentTicks - lastticks, 100);
TimePoint currentTicks = Clock::now();
Duration timeElapsed = currentTicks - lastTimePoint;
if(timeElapsed > boost::chrono::milliseconds(100))
timeElapsed = boost::chrono::milliseconds(100);
lastticks = SDL_GetTicks();
lastTimePoint = currentTicks;
lastFrameIndex = (lastFrameIndex + 1) % lastFrameTimes.size();
lastFrameTimes[lastFrameIndex] = timeElapsed;
}
accumulatedTime += timeElapsed;
if(accumulatedFrames >= 100)
ui32 FramerateManager::getElapsedMilliseconds() const
{
//about 2 second should be passed
fps = static_cast<int>(ceil(1000.0 / (accumulatedTime / accumulatedFrames)));
accumulatedTime = 0;
accumulatedFrames = 0;
}
return lastFrameTimes[lastFrameIndex] / boost::chrono::milliseconds(1);
}
ui32 FramerateManager::getFramerate() const
{
Duration accumulatedTime = std::accumulate(lastFrameTimes.begin(), lastFrameTimes.end(), Duration());
auto actualFrameTime = accumulatedTime / lastFrameTimes.size();
if(actualFrameTime == actualFrameTime.zero())
return 0;
return std::round(boost::chrono::duration<double>(1) / actualFrameTime);
};

View File

@ -9,23 +9,32 @@
*/
#pragma once
// A fps manager which holds game updates at a constant rate
/// Framerate manager controls current game frame rate by constantly trying to reach targeted frame rate
class FramerateManager
{
private:
double rateticks;
ui32 lastticks;
ui32 timeElapsed;
int rate;
int fps; // the actual fps value
ui32 accumulatedTime;
ui32 accumulatedFrames;
using Clock = boost::chrono::high_resolution_clock;
using TimePoint = Clock::time_point;
using Duration = Clock::duration;
/// cyclic buffer of durations of last frames
std::array<Duration, 60> lastFrameTimes;
Duration targetFrameTime;
TimePoint lastTimePoint;
/// index of last measured frome in lastFrameTimes array
ui32 lastFrameIndex;
public:
FramerateManager(int newRate); // initializes the manager with a given fps rate
void init(int newRate); // needs to be called directly before the main game loop to reset the internal timer
void framerateDelay(); // needs to be called every game update cycle
ui32 getElapsedMilliseconds() const {return this->timeElapsed;}
ui32 getFrameNumber() const { return accumulatedFrames; }
ui32 getFramerate() const { return fps; };
FramerateManager(int targetFramerate);
/// must be called every frame
/// updates framerate calculations and executes sleep to maintain target frame rate
void framerateDelay();
/// returns duration of last frame in seconds
ui32 getElapsedMilliseconds() const;
/// returns current estimation of frame rate
ui32 getFramerate() const;
};

View File

@ -163,16 +163,6 @@ void CBuildingRect::clickRight(tribool down, bool previousState)
}
}
SDL_Color multiplyColors(const SDL_Color & b, const SDL_Color & a, double f)
{
SDL_Color ret;
ret.r = static_cast<uint8_t>(a.r * f + b.r * (1 - f));
ret.g = static_cast<uint8_t>(a.g * f + b.g * (1 - f));
ret.b = static_cast<uint8_t>(a.b * f + b.b * (1 - f));
ret.a = static_cast<uint8_t>(a.a * f + b.b * (1 - f));
return ret;
}
void CBuildingRect::show(SDL_Surface * to)
{
uint32_t stageDelay = BUILDING_APPEAR_TIMEPOINT;