File: FrameTimer.cpp

package info (click to toggle)
endless-sky 0.10.16-3
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 414,608 kB
  • sloc: cpp: 73,435; python: 893; xml: 666; sh: 271; makefile: 28
file content (108 lines) | stat: -rw-r--r-- 3,118 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
/* FrameTimer.cpp
Copyright (c) 2014 by Michael Zahniser

Endless Sky is free software: you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or (at your option) any later version.

Endless Sky is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with
this program. If not, see <https://www.gnu.org/licenses/>.
*/

#include "FrameTimer.h"

#ifdef _WIN32
#define STRICT
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#else
#include <thread>
#endif

using namespace std;



// Create a timer that is just responsible for measuring the time that
// elapses until Time() is called.
FrameTimer::FrameTimer()
{
	next = chrono::steady_clock::now();
}



// Create a frame timer that will space frames out at exactly the given FPS,
// _unless_ a frame takes too long by at least the given lag, in which case
// the next frame happens immediately but no "catch-up" is done.
FrameTimer::FrameTimer(int fps, int maxLagMsec)
	: step(chrono::nanoseconds(1000000000 / fps)),
	maxLag(chrono::milliseconds(maxLagMsec))
{
	next = chrono::steady_clock::now();
	Step();
}



// Wait until the next frame should begin.
void FrameTimer::Wait()
{
	// Note: in theory this could get interrupted by a signal handler, although
	// it's unlikely the program will receive any signals that do not terminate
	// it and that it does not ignore. But, the worst that would happen in that
	// case is that this particular frame will end too quickly, and then it will
	// go back to normal for the next one.
	chrono::steady_clock::time_point now = chrono::steady_clock::now();
	if(now < next)
	{
		// This should never happen with a true steady clock, but make sure that
		// the sleep time is never longer than one frame.
		if(now + step + maxLag < next)
			next = now + step;

		// The Winpthreads implementation of sleep on MinGW > 8 is inaccurate when
		// compared to the native Windows Sleep function.
		// See the thread starting with https://sourceforge.net/p/mingw-w64/mailman/message/37013810/.
#ifdef _WIN32
		Sleep(chrono::duration_cast<chrono::milliseconds>(next - now).count());
#else
		this_thread::sleep_until(next);
#endif
		now = chrono::steady_clock::now();
	}
	// If the lag is too high, don't try to do catch-up.
	if(now - next > maxLag)
		next = now;

	Step();
}



// Find out how long it has been since this timer was created, in seconds.
double FrameTimer::Time() const
{
	chrono::steady_clock::time_point now = chrono::steady_clock::now();
	return chrono::duration_cast<chrono::nanoseconds>(now - next).count() * .000000001;
}



// Change the frame rate (for viewing in slow motion).
void FrameTimer::SetFrameRate(int fps)
{
	step = chrono::nanoseconds(1000000000 / fps);
}



// Calculate when the next frame should begin.
void FrameTimer::Step()
{
	next += step;
}