Jump to content
  • Advertisement
Sign in to follow this  
Sythical

Timer class not working properly

This topic is 2208 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

Hello, I've written a timer class in C++ but it's not working properly. The trouble I'm having is that after a couple of minutes the animation becomes choppy. It sometimes fixes automatically after about 5 ~ 60 seconds.

#include <windows.h>

class Timer {

	int sleep_length_ms;
	double ratio;
	LONGLONG duration, next_mark;
	LARGE_INTEGER counter, frequency;

public:
	Timer(int frame_rate) {
		QueryPerformanceCounter(&counter);
		QueryPerformanceFrequency(&frequency);

		duration = frequency.QuadPart / frame_rate;
		next_mark = counter.QuadPart + duration;
		ratio = 1000.0 / frequency.QuadPart;
	}

	bool wait() {
		QueryPerformanceCounter(&counter);
		sleep_length_ms = (int) (ratio * (next_mark - counter.QuadPart));

		if(sleep_length_ms > 0) {
			Sleep(sleep_length_ms);
			next_mark += duration;					
		} else {
			// if running behind
			QueryPerformanceCounter(&counter);
			next_mark = counter.QuadPart + duration;
		}

		return true;
	}
};

This is how the class if used in a game loop:

Timer timer(60);

while(run_game) {
	update_game();
	render_game();

	timer.wait();
}

I'm testing this class using a simple Allegro program where a circle bounces off the edges. I tried using the timer class provided by Allegro and the issue resolves so I'm quite sure that the problem lies within my timer class. I can't seem to figure out what I'm doing wrong and would be very grateful if someone can have a read through the code. Thank you!

Share this post


Link to post
Share on other sites
Advertisement

Sleep is evil. AFAIK the resolution of Sleep is that of the task scheduler timer. 20ms? Searching for that topic should help you out.

Share this post


Link to post
Share on other sites

I know that Sleep provides no guarantee that the thread will "wake up" after the specified duration but I also don't want to rely on a do while loop for timing. I read through the relavent source files of Allegro and it also uses Sleep in the timer thread and it seems to work perfectly.

Share this post


Link to post
Share on other sites

Add a check code. sleep_length_ms cannot be longer than the period of the timer. for your example 16ms. So if it is larger than 0 but even larger than the timer period something goes wrong.

Share this post


Link to post
Share on other sites

Here is mine. I use the "HasElapsed" function to do things periodically in a main loop. For example, you can send a packet every 20ms by calling HasElapsed.

#pragma once
class GameTimer
{
public:
	GameTimer(void);
	~GameTimer(void);

	void  Reset();
	float Tick();
	bool HasElapsed(float milliseconds);

private:
	bool    IsPerfCounterAvailable;
	float   TimeScale;
	__int64 PerfCounterFrequency;
	__int64 LastTime;
	__int64 CurrentTime;	
};

GameTimer::GameTimer(void)
{
	//Check if a High resolution timer is available 
	if(QueryPerformanceFrequency((LARGE_INTEGER*)&PerfCounterFrequency)){ 
		IsPerfCounterAvailable = true;
		QueryPerformanceCounter((LARGE_INTEGER*)&CurrentTime); 
		TimeScale = 1.0f / PerfCounterFrequency;
	} else { 
		IsPerfCounterAvailable = false;
		CurrentTime = timeGetTime(); 
		TimeScale	= 0.001f;
    } 
	
	Reset();
}


GameTimer::~GameTimer(void)
{
}

bool GameTimer::HasElapsed(float milliseconds)
{
	if(IsPerfCounterAvailable){
		QueryPerformanceCounter((LARGE_INTEGER*)&CurrentTime);
	} else {
		CurrentTime = timeGetTime();
	}

	if( ((CurrentTime - LastTime) * TimeScale) > (milliseconds*0.001f))
	{
		LastTime = CurrentTime;
		return true;
	}

	return false;
}

float GameTimer::Tick()
{
	LastTime = CurrentTime;

	if(IsPerfCounterAvailable){
		QueryPerformanceCounter((LARGE_INTEGER*)&CurrentTime);
	} else {
		CurrentTime = timeGetTime();
	}

	// Calculate the elapsed time
	float ElapsedTime = (CurrentTime - LastTime) * TimeScale;

	return ElapsedTime;
}

void GameTimer::Reset()
{
	if(IsPerfCounterAvailable){
		QueryPerformanceCounter((LARGE_INTEGER*)&CurrentTime);
	} else {
		CurrentTime = timeGetTime();
	}

	// Initialization
	LastTime = CurrentTime;
}

Share this post


Link to post
Share on other sites

Thank you for the suggestion. I just tried that and sleep_length_ms fluctuates between 16 and 17 so that seems fine. When the animation finally starts to stutter, sleep_length_ms is still 16 or 17. I also added a check to let me know when Sleep() makes the thread oversleep and that doesn't seem to be the problem either. So confusing, it's probably a silly mistake somewhere that I can't spot.

Share this post


Link to post
Share on other sites

Try this with my timer. 

GameTimer FrameTimer;
while(run_game) {
if(FrameTimer.HasElapsed(60))       //Render every 60ms
{
	update_game();
	render_game();
}
else
{
Sleep(0)
 }
}
Edited by Tispe

Share this post


Link to post
Share on other sites

I was using something similar to the code you posted but the CPU usage was around 25%. I know that there isn't exactly anything wrong with that and it's normal for games but I wanted to try and write a CPU efficient timer.

Share this post


Link to post
Share on other sites

You can maybe get away with using Sleep(1).

 

I would decouple update_game() from render_game(). Such that update_game() works independently on FPS. Which means you need to pass it the elapsed time every time you call it and only update incrementally based on elapsed time. That way animation will be smooth and consistant regardless of FPS. This will cure your stuttering problems.

GameTimer FrameTimer;
GameTimer UpdateTimer;
while(run_game) {

update_game(UpdateTimer.Tick());

if(FrameTimer.HasElapsed(20)) {
	render_game();
} else {
Sleep(1);
 }

}
Edited by Tispe

Share this post


Link to post
Share on other sites

I considered using Sleep(1) but I believe it'll make thread oversleep fairly often. Another thing I tried was make the thread sleep for 70% of the required time and then rely on a do while loop for the remainder of the duration. That was fairly accurate and the dropped the usage to 10%. I'll follow your advice and decouple the physics/logic and rendering, I have read the fix your timestep article and implement that but my GPU's fan started making an unpleasant noise (the whole reason why I got obsessed with CPU/GPU usage).

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.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!