Hi all,
I've been strugling to experiment with getting a constant fixed framerate.
Debugging so far didn't bring up the problem.
In short, this is what I'm doing (note; I know there are other/ better/ different ways to manage time steps, but I just want to know why this doesn't work):
- once: set fixed frametime, 1 / xx, i.e. 1/60 = 16.67ms
- each iteration
-- check how long last frame took
-- if < fixed frametime: wait till fixed time has passed
-- if > fixed frametime: skip frame aka wait till next fixed frametime has passed
-- update game logics using fixed deltaT
-- render
My debugging results show what I expect, some samples:
deltaT: 12.493622 ms
Currtime: 11748114, waitTill: 11748118
deltaT: 14.139329 ms
Currtime: 11748128, waitTill: 11748130
deltaT: 11.998593 ms
Currtime: 11748140, waitTill: 11748144
deltaT: 14.002699 ms
Currtime: 11748154, waitTill: 11748156
deltaT: 12.041601 ms
Currtime: 11748166, waitTill: 11748170
Though 'rounded', the waiting time and deltaT nicely add up to the fixed frametime.
The issue:
- with vsync enabled it's all good
- with vsync enabled I get jumping fps/ frametimes, between 45 and 80 (and in release mode up to 120fps...)
Below the code of what I'm doing.
Any input is really appreciated.
int CAppl::Run()
{
MSG msg = {0};
mTimer.Reset();
mTimer.Tick();
float fixedDeltaT = 1000.0f/60.0f; // 60fps - 16.67ms per frame
bool run = true;
while(run)
{
mInputHandler.UpdateKeys();
while(PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
if(msg.message == WM_QUIT) run = false;
}
if(!mAppPaused )
{
mTimer.Tick();
CalculateFrameStats();
float waitTime = 0.0f;
if(mTimer.GetDeltaTime() < fixedDeltaT)
{
waitTime = fixedDeltaT - mTimer.GetDeltaTime(); // time left: wait till fixed frametime is passed
}
if(mTimer.GetDeltaTime() > fixedDeltaT)
{
waitTime = fixedDeltaT - fmod(mTimer.GetDeltaTime(), fixedDeltaT); // time exceeded: wait till next frametime is passed
}
if(waitTime > (fixedDeltaT - 1.0f) || waitTime < 1.0f) waitTime = 0.0f;
uint64_t startWaitTime = mTimer.GetCurrTime();
// DEBUGGING
char tempText[100];
//sprintf_s(tempText, "Currtime: %u\n", startWaitTime);
//OutputDebugStringA(tempText);
/* sprintf_s(tempText, "deltaT: %f ms\n", mTimer.GetDeltaTime());
OutputDebugStringA(tempText);
sprintf_s(tempText, "waittime: %f ms\n", waitTime);
OutputDebugStringA(tempText); */
// CONTINUE
uint64_t waitTill = startWaitTime + static_cast<uint64_t>(waitTime);
sprintf_s(tempText, "Currtime: %u, waitTill: %u\n", static_cast<uint>(startWaitTime), static_cast<uint>(waitTill));
OutputDebugStringA(tempText);
while(mTimer.GetCurrTime() <= waitTill) { }
if(!Update(fixedDeltaT, mTimer.GetTotalTime()))
{
CLOG(FATAL, "GAMEBASE") << fatal_update_appl.c_str();
return(int)msg.wParam;
}
if(!Render())
{
CLOG(FATAL, "GAMEBASE") << fatal_render_appl.c_str();
return(int)msg.wParam;
}
}
else
{
Sleep(100);
}
}
return (int)msg.wParam;
}
void CGameTimer::Tick()
{
std::chrono::high_resolution_clock::time_point currTime = std::chrono::high_resolution_clock::now();
std::chrono::duration<double, std::milli> delta = currTime - mPrevTime;
mDeltaTime = delta.count();
mPrevTime = currTime;
// force nonnegative: if procesor goes into power save or sth., delta could be negative
if(mDeltaTime < 0.0)
{
mDeltaTime = 0.0;
}
}
uint64_t CGameTimer::GetTotalTime() const
{
std::chrono::duration<double> addTime = std::chrono::high_resolution_clock::now() - mStartTime;
return static_cast<uint64_t>(addTime.count());
}
float CGameTimer::GetDeltaTime() const
{
return static_cast<float>(mDeltaTime);
}
uint64_t CGameTimer::GetCurrTime() const
{
return std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now().time_since_epoch()).count();
}