3
\$\begingroup\$

EDIT: [SOLUTION] I posted a possibly explanation for my issue below to anyone who has a similar problem. Hope it helps.

I'm coding a rhythm game and the game runs smoothly with uncapped fps. But when I try to cap it around 60 the game updates in little chunks, like hiccups, as if it was skipping frames or at a very low frame rate. The reason I need to cap frame rate is because in some computers I tested, the fps varies a lot (from ~80 - ~250 fps) and those drops are noticeable and degrade response time. Since this is a rhythm game this is very important.

This issue is driving me crazy. I've spent a few weeks already on it and still can't figure out the problem. I hope someone more experienced than me could shed some light on it. I'll try to put here all the hints I've tried along with two pseudo codes for game loops I tried, so I apologize if this post gets too lengthy.

1st GameLoop:

const uint UPDATE_SKIP = 1000 / 60;
uint nextGameTick = SDL_GetTicks();

while(isNotDone)
{

    // only false when a QUIT event is generated!
    if (processEvents())
    {
        if (SDL_GetTicks() > nextGameTick)
        {
            update(UPDATE_SKIP);
            render();
            nextGameTick += UPDATE_SKIP;
        }
    }
}

2nd Game Loop:

    const uint UPDATE_SKIP = 1000 / 60;
    while (isNotDone)
    {
    LARGE_INTEGER startTime;
    QueryPerformanceCounter(&startTime);

    // process events will return false in case of a QUIT event processed
    if (processEvents())
    {
        update(frameTime);
        render();   
    }

    LARGE_INTEGER endTime;
    do {
        QueryPerformanceCounter(&endTime);
        frameTime = static_cast<uint>((endTime.QuadPart - startTime.QuadPart) * 1000.0 / frequency.QuadPart);
    } while (frameTime < UPDATE_SKIP);
    }

[1] At first I thought it was a timer resolution problem. I was using SDL_GetTicks, but even when I switched to QueryPerformanceCounter, supposedly less granular, I saw no difference.

[2] Then I thought it could be due to a rounding error in my position computation and since game updates are smaller in high FPS that would be less noticeable. Indeed there is an small error, but from my tests I realized that it is not enough to produce the position jumps I'm getting. Also, another intriguing factor is that if I enable vsync I'll get smooth updates @60fps regardless frame cap code. So why not rely on vsync? Because some computers can force a disable on gfx card config.

[3] I started printing the maximum and minimum frame time measured in 1sec span, in the hope that every a few frames one would take a long time but still not enough to drop my fps computation. It turns out that, with frame cap code I always get frame times in the range of [16, 18]ms, and still, the game "does not moves like jagger".

[4] My process' priority is set to HIGH (Windows doesn't allow me to set REALTIME for some reason). As far as I know there is only one thread running along with the game (a sound callback, which I really don't have access to it). I'm using AudiereLib. I then disabled Audiere by removing it from the project and still got the issue. Maybe there are some others threads running and one of them is taking too long to come back right in between when I measured frame times, I don't know. Is there a way to know which threads are attached to my process?

[5] There are some dynamic data being created during game run. But It is a little bit hard to remove it to test. Maybe I'll have to try harder this one.

Well, as I told you I really don't know what to try next. Anything, I mean, anything would be of great help. What bugs me more is why at 60fps & vsync enabled I get an smooth update and at 60fps & no vsync I don't. Is there a way to implement software vsync? I mean, query display sync info?

Thanks in advance. I appreciate the ones that got this far and yet again I apologize for the long post.

Best Regards from a fellow coder.

\$\endgroup\$
1

3 Answers 3

3
\$\begingroup\$

My strong suspicion is that your problem is in your usage of UPDATE_SKIP. When you define:

const uint UPDATE_SKIP = 1000 / 60;

what you're actually saying is

const uint UPDATE_SKIP = 16;

—but the problem is that there's no number n that has 16*n=1000! Essentially, while you may be clamping yourself to 60Hz elsewhere, here in these game loops you're actually running one frame (one call to update()) per 16ms—and those aren't the same thing. That disconnect (which would also occur with UPDATE_SKIP = 1000/30, since that also isn't an even division) is probably the source of your issue, but without the rest of your code it's hard to say precisely what the correct fix would be - hopefully this will give you more to look at, though...

\$\endgroup\$
2
  • \$\begingroup\$ Try changing to a float. \$\endgroup\$
    – Amplify91
    Jul 9, 2012 at 17:14
  • \$\begingroup\$ The imprecision is correctly in the next frame. Say I measure 16ms where I should have 16.666 if UPDATE_SKIP were float. Next frame I'll get 17ms. Anyway, I changed the game interface to accept float and just as I suspected it didn't fix it. I've kind of came up with a workaround for it that I'll post in the question as an edit to anyone who has a similar problem. \$\endgroup\$ Jul 12, 2012 at 15:50
1
\$\begingroup\$

Well, after a lot of more tests I realized that the problem was either in the way I was measuring time, which I find really unlikely due to the fact that I've tested many timers, or something in the graphics card that was preventing it from rendering to screen, like it was skipping frames.

I found the exact term for that and it made a lot easier to search for the solution after. It's called (macro/micro) stuttering. It can be caused by a whole variety of this, not related to your game (yay! the art of PC game programming).

It could be due to an anti-virus running, instant message programs interference, multi-GPU sincronization (micro stuttering), laptop clock variety due to power consumption settings, intel processors can give errors due to "intel speed stepping" that changes cpu clock based on cpu usage (that would probably mess up with my QFC timer) http://www.intel.com/cd/channel/reseller/ASMO-NA/ENG/203838.htm, it could be a driver problem (nvidia as of today 07/12/12 has a driver bug that causes micro-stuttering in single GPU for GTX 6 card family): http://videocardz.com/33159/nvidia-v-sync-stuttering-caused-by-drivers-bug-fix-coming

Well in my case I realized another particularity, as stated before that enabling vsync fixes the issue. What is probably happening is a tearing effect that gives the illusion of a frame skip, since the camera is fixed I don't see the "tear line", only notes scrolling weirdly. I can't really tell if a tearing is happening because it scrolls so fast, but I'm accepting it for now as vsync fixes the issue.

I hope it helps someone out there struggling with this problem.

\$\endgroup\$
0
\$\begingroup\$

You should enable VSync. If you are worried with computers forcing it off, add a cap just to make sure. Without VSync, a buffer can be flip (assuming you are using double buffer, if you are not, you really should) while being rendered (I mean, the monitor is reading and displaying it), resulting in an image which part of it is from the previous frame.

Note that for most games, since 99.9% of time one frame is not very different from the next frame, this event is not very visible, but in one game I worked on, when VSync was off, I got an interesting artifact (it was the PC port of an iPhone game, so I was getting like 3000 fps), were a camera shaking algorithm was being applied for different values (since it changed every frame) for different parts of the screen.

\$\endgroup\$
1
  • \$\begingroup\$ fbafelipe, Thanks for the quick reply. I'm aware of the tearing effect that having vsync disabled can produce. I have no intention to let it off. The problem is exactly when I try to cap frame rate, I get a choppy update, and it doesn't matter if vsync is on or off. If I remove the do-while code from gameloop2 (call code body only once) I get a smooth update, but uncapped frame rate. If I let do-while on I get a choopy update. \$\endgroup\$ Jul 6, 2012 at 19:59

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .