-
Notifications
You must be signed in to change notification settings - Fork 2
/
hertz.hpp
129 lines (108 loc) · 3.78 KB
/
hertz.hpp
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
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
// Hertz, simple framerate locker. based on code by /u/concavator (ref: http://goo.gl/Ry50A4)
// - rlyeh. zlib/libpng licensed
//
// features:
// [x] auto-frameskip
// [x] dynamic/variable framerate locking
#pragma once
#include <cmath>
#include <chrono>
#include <thread>
#define HERTZ_VERSION "1.0.1" /* (2015/12/05) Add unlock function
#define HERTZ_VERSION "1.0.0" // (2015/09/19) Initial commit */
namespace hertz {
// function that locks your logic and render to desired framerate (in HZ).
// returns number of current fps
template<typename FNU, typename FNR>
static inline
double lock( signed HZ, FNU &update, FNR &render ) {
// rw vars
static volatile unsigned hz = 60, isGameRunning = 1, maxframeskip = 10;
// ro vars
static volatile unsigned fps = 0;
// private vars
static volatile unsigned timer_counter = 0, loop_counter = 0;
// private threaded timer
static struct install {
install() {
std::thread([&]{
std::chrono::microseconds acc( 0 ), third( 300000 );
while( isGameRunning ) {
// update timer
timer_counter++;
std::chrono::microseconds duration( int(1000000/hz) );
std::this_thread::sleep_for( duration );
// update fps 3 times per second
acc += duration;
if( acc >= third ) {
acc -= acc;
static int before = loop_counter;
fps = int( std::round( (loop_counter - before) * 3.3333333 ) );
before = loop_counter;
}
}
isGameRunning = 1;
}).detach();
}
} timer;
if( HZ < 0 ) {
isGameRunning = 0;
return 1;
}
hz = HZ > 0 ? HZ : hz;
// we got too far ahead, cpu idle wait
while( loop_counter > timer_counter && isGameRunning ) {
std::this_thread::yield();
}
// max auto frameskip is 10, ie, even if speed is low paint at least one frame every 10
if( timer_counter > loop_counter + 10 ) {
timer_counter = loop_counter;
}
loop_counter++;
// only draw if we are fast enough, otherwise skip the frame
update();
if( loop_counter >= timer_counter ) {
render();
}
return fps;
}
static inline void unlock() {
auto nil = []{};
hertz::lock( -1, nil, nil );
}
}
#ifdef HERTZ_BUILD_SAMPLE
#ifdef _WIN32
#include <windows.h>
#pragma comment(lib, "user32.lib")
#endif
#include <algorithm>
#include <iostream>
#include <time.h>
#include <stdio.h>
int main() {
unsigned HZ = 60, updates = 0, frames = 0, fps = 0;
auto update = [&]{
updates++;
#ifdef _WIN32
static auto &once = std::cout << "Keys: up, down, escape, space" << std::endl;
if( GetAsyncKeyState(VK_UP) & 0x8000 ) HZ+=(std::min)((std::max)(int(HZ*0.01), 1), 5);
if( GetAsyncKeyState(VK_DOWN) & 0x8000 ) if(HZ > (std::max)(int(HZ*0.01), 1)) HZ-=(std::max)(int(HZ*0.01), 1);
if( GetAsyncKeyState(VK_ESCAPE) & 0x8000 ) exit(0);
if( GetAsyncKeyState(VK_SPACE) & 0x8000 ) Sleep( rand() % 80 );
#endif
};
auto render = [&]{
frames++;
char bar[] = "\\|/-";
auto current_time = time(NULL);
auto sec = localtime(¤t_time)->tm_sec;
printf( "(%d/%d) [%c] updates %02ds [%c] frames \r",
fps, HZ, bar[updates%4], sec, bar[frames%4] );
};
for(;;) {
fps = hertz::lock( HZ, update, render );
}
hertz::unlock();
}
#endif