Explorar o código

FramerateManager now uses chrono instead of SDL_Ticks

Ivan Savenko %!s(int64=2) %!d(string=hai) anos
pai
achega
3ecdff2a21

+ 0 - 2
client/CMT.cpp

@@ -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
 	{

+ 2 - 0
client/gui/CGuiHandler.cpp

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

+ 6 - 0
client/gui/CGuiHandler.h

@@ -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

+ 32 - 40
client/gui/FramerateManager.cpp

@@ -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)
-{
-	init(newRate);
-}
-
-void FramerateManager::init(int newRate)
+FramerateManager::FramerateManager(int targetFrameRate)
+	: targetFrameTime(Duration(boost::chrono::seconds(1)) / targetFrameRate)
+	, lastFrameIndex(0)
+	, lastFrameTimes({})
+	, lastTimePoint (Clock::now())
 {
-	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);
+
+	lastTimePoint = currentTicks;
+	lastFrameIndex = (lastFrameIndex + 1) % lastFrameTimes.size();
+	lastFrameTimes[lastFrameIndex] = timeElapsed;
+}
 
-	lastticks = SDL_GetTicks();
+ui32 FramerateManager::getElapsedMilliseconds() const
+{
+	return lastFrameTimes[lastFrameIndex] / boost::chrono::milliseconds(1);
+}
 
-	accumulatedTime += timeElapsed;
+ui32 FramerateManager::getFramerate() const
+{
+	Duration accumulatedTime = std::accumulate(lastFrameTimes.begin(), lastFrameTimes.end(), Duration());
 
-	if(accumulatedFrames >= 100)
-	{
-		//about 2 second should be passed
-		fps = static_cast<int>(ceil(1000.0 / (accumulatedTime / accumulatedFrames)));
-		accumulatedTime = 0;
-		accumulatedFrames = 0;
-	}
-}
+	auto actualFrameTime = accumulatedTime / lastFrameTimes.size();
+	if(actualFrameTime == actualFrameTime.zero())
+		return 0;
+
+	return std::round(boost::chrono::duration<double>(1) / actualFrameTime);
+};

+ 24 - 15
client/gui/FramerateManager.h

@@ -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;
 };

+ 0 - 10
client/windows/CCastleInterface.cpp

@@ -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;