Reputation: 526
I wrote the code bellow using std::chrono c++ library , what i am trying to do is
to fix the application's FPS
on 60 , but i am getting 50 FPS
, not a performance issue for sure
because i am computing nothing . but it is certainly an invalid usage or a bug .
the TARGET_FPS
macro is set to the target FPS
that i want to get , then the console window
displays the real actual FPS , these following lines shows the values i set TARGET_FPS
to , and each is associated to the final FPS
.
TARGET_FPS---->FPS
60----->50
90----->50
100----->100
1000----->100
10000----->100
whatever ----->100
Even if i define TARGET_FPS
to 1000000000 i get 100 FPS
, even when i define it to 458 or whatever value more than 100 i will get 100 FPS
as output .
#include <chrono> /// to use std::chrono namespace
#include <iostream> /// for console output
#include <thread> /// for std::this_thread::sleep_for()
#define TARGET_FPS 60// our target FPS
using frame_len_type = std::chrono::duration<float,std::ratio<1,TARGET_FPS>>; /// this is the duration that defines the length of a frame
using fsecond = std::chrono::duration<float>; /// this duration represents once second and uses 'float' type as internal representation
const frame_len_type target_frame_len(1); /// we will define this constant here , to represent on frame duration ( defined to avoid construction inside a loop )
void app_logic(){ /** ... All application logic goes here ... **/}
int main() /// our main function !
{
using sys_clock = std::chrono::system_clock; /// simplify the type name to make the code readable
sys_clock::time_point frame_begin,frame_end; /// we will use these time points to point to frame begin and end
while (true)
{
frame_begin = sys_clock::now(); /// there we go !
app_logic(); /// lets be logical here :)
frame_end = sys_clock::now(); /// we are done so quick !
std::this_thread::sleep_for( target_frame_len- (frame_end.time_since_epoch()-frame_begin.time_since_epoch()) ); /// we will take a rest that is equal to what we where supposed to take to finish the actual target frame length
std::cout<< fsecond(1) / ( sys_clock::now() - frame_begin) <<std::endl; /// this will show ass the current FPS
}
return 0; /// return to OS
} /// end of code
Upvotes: 2
Views: 1287
Reputation: 526
Problem solved :)
#include <chrono> /// to use std::chrono namespace
#include <iostream> /// for console output
#include <thread> /// for std::this_thread::sleep_for()
#include <windows.h>
#define TARGET_FPS 500 /// our target fps as a macro
const float target_fps = (float)TARGET_FPS; /// our target fps
float tmp_target_fps = target_fps; /// used to adjust the target fps depending on the actual real fps to reach the real target fps
using frame_len_type = std::chrono::duration<float,std::ratio<1,TARGET_FPS>>; /// this is the duration that defines the length of a frame
using fsecond = std::chrono::duration<float>; /// this duration represents once second and uses 'float' type as internal representation
fsecond target_frame_len(1.0f/tmp_target_fps); /// we will define this constant here , to represent on frame duration ( defined to avoid construction inside a loop )
bool enable_fps_oscillation = true;
void app_logic()
{
/** ... All application logic goes here ... **/
}
class HeighResolutionClockKeeper
{
private :
bool using_higher_res_timer;
public :
HeighResolutionClockKeeper() : using_higher_res_timer(false) {}
void QueryHeighResolutionClock()
{
if (timeBeginPeriod(1) != TIMERR_NOCANDO)
{
using_higher_res_timer = true;
}
}
void FreeHeighResolutionClock()
{
if (using_higher_res_timer)
{
timeEndPeriod(1);
}
}
~HeighResolutionClockKeeper()
{
FreeHeighResolutionClock(); /// if exception is thrown , if not this wont cause problems thanks to the flag we put
}
};
int main() /// our main function !
{
HeighResolutionClockKeeper MyHeighResolutionClockKeeper;
MyHeighResolutionClockKeeper.QueryHeighResolutionClock();
using sys_clock = std::chrono::system_clock; /// simplify the type name to make the code readable
sys_clock::time_point frame_begin,frame_end; /// we will use these time points to point to frame begin and end
sys_clock::time_point start_point = sys_clock::now();
float accum_fps = 0.0f;
int frames_count = 0;
while (true)
{
frame_begin = sys_clock::now(); /// there we go !
app_logic(); /// lets be logical here :)
frame_end = sys_clock::now(); /// we are done so quick !
std::this_thread::sleep_for( target_frame_len- (frame_end.time_since_epoch()-frame_begin.time_since_epoch()) ); /// we will take a rest that is equal to what we where supposed to take to finish the actual target frame length
float fps = fsecond(1) / ( sys_clock::now() - frame_begin) ; /// this will show ass the current FPS
/// obviously we will not be able to hit the exact FPS we want se we need to oscillate around until we
/// get a very close average FPS by time .
if (fps < target_fps) /// our real fps is less than what we want
tmp_target_fps += 0.01; /// lets ask for more !
else if (fps > target_fps ) /// it is more than what we want
tmp_target_fps -=0.01; /// lets ask for less
if(enable_fps_oscillation == true)
{
/// now we will adjust our target frame length for match the new target FPS
target_frame_len = fsecond(1.0f/tmp_target_fps);
/// used to calculate average FPS
accum_fps+=fps;
frames_count++;
/// each 1 second
if( (sys_clock::now()-start_point)>fsecond(1.0f)) /// show average each 1 sec
{
start_point=sys_clock::now();
std::cout<<accum_fps/frames_count<<std::endl; /// it is getting more close each time to our target FPS
}
}
else
{
/// each frame
std::cout<<fps<<std::endl;
}
}
MyHeighResolutionClockKeeper.FreeHeighResolutionClock();
return 0; /// return to OS
} /// end of code
I had to add timeBeginPeriod()
and timeEndPeriod()
on windows platform , thanks to this awesome , lost-in-the-wind website http://www.geisswerks.com/ryan/FAQS/timing.html from Ryan Geiss .
Details :
Because we can't actually hit the exact fps that we want ( very slightly above or bellow , but up to 1000 fps and down to 1 fps thanks to timeXPeriod(1) ) therefore i used some extra dump fps variable to adjust the target fps i am seeking, increasing it and decreasing it .., that will let us control the actual application fps to hits our real target fps as an average (you can enable and disable this using 'enable_fps_oscillation' flag ) this fixes an issue for fps = 60 because we can't hit it ( +/-0.5 ) , but if we set fps = 500 we hit it and we dont need to oscillate bellow and above it
Upvotes: 2
Reputation: 73366
The timing resolution of std::chrono is system dependent:
After the sleep interval has passed, the thread is ready to run. If you specify 0 milliseconds, the thread will relinquish the remainder of its time slice but remain ready. Note that a ready thread is not guaranteed to run immediately. Consequently, the thread may not run until some time after the sleep interval elapses.
sleep_for
, whatever OS you are using: 30.3.2/7: Effect: Blocks the calling thread for the relative timeout (...)
Consequence:
app_logic()
is ultra fast, your thread will sleep at least 15.6 ms. If the logic takes 1 ms to execute, you'd be exactly at 60 FPS. However, according to the API documentation, if [wait time] is greater than one tick but less than two, the wait can be anywhere between one and two ticks, so that the average sleep time will be between 15.6 and 31.2 ms, whic means, inversely, that your FPS will be between 60 and 32 FPS. This explains why you only achieve 50 FPS.
When you set FPS to 100, there should be a frame every 10ms. This is below the timer accuracy. There might be no sleep at all. If no other thread is ready to run, the function will return immediately, so that you will be at your maximum throughput. If you set a higher FPS, you'd be in exactly the same situation as the expected waiting time would always be below the timer accuracy. The result will therefore not improve.
Upvotes: 2