Jump to content
  • Advertisement
Sign in to follow this  
cozzie

Fixed frametime

This topic is 518 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

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();
}

Share this post


Link to post
Share on other sites
Advertisement

getting a constant fixed framerate.

 

a simple framerate limiter will get you a fixed framerate - bot only on hardware that's fast enough.  if the hardware is not fast enough, you'll get a lower FPS, which will vary as the computational load varies.

 

ET accumulator and DT type stuff is only required for things like fix-your-timestep - IE algos that "decouple" (IE un-lockstep) render from update.

Share this post


Link to post
Share on other sites
I haven't deeply examinated your code, but don't use std::chrono. In VS2012 & 2013 it wasn't accurate at all. I don't know if they fixed it in VS 2015.

I had no issues with high_resolution_clock (and/or as they are the same steady clock) on VS 2013 and 2015.

Share this post


Link to post
Share on other sites

Keep in mind that the graphics driver/api/OS will buffer frames as well. How your manual throttling interacts with this can be a huge source of issues. If you're attempting to minimize latency, the OS will do everything in its power to prevent that and try to buffer frames!

There is a really great tool for checking this, I think GpuView (someone please correct me if I'm wrong!).

Share this post


Link to post
Share on other sites

Just for reference; I've decided to stop trying to get this 'practice' running.

Since I wont be using it later on and the efforts are not worth the results.

Know 'simply' going for the static updating rate and 'free' rendering rate (like the Fix your timestep approach).

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!