I'm having a bit of a problem concerning the main game loop code in my engine. Long story short, I wrote a (relatively simple) tile-based game engine in C++ using Allegro 4.4 and AllegroGL. Now that it's actually close to being finished (design-wise at least), I found a bug involving fullscreen rendering and it is giving me a headache ever since. I'll try to explain what the problem is as clearly as I can, or what I've managed to make of it so far.
Simply put, the problem is that the game stutters when in fullscreen mode. It feels like rendering is falling behind on logic, and it has to keep up by kind of "shaking" the screen every few moments. It's subtle, but noticeable. I have a scrolling screen mechanism in my engine/game, and the effect is rather obvious when the game screen is moving about (this is why I never noticed this until I actually had a huge amount of work already done). It is interesting that this only happens in fullscreen. In windowed mode the rendering is perfect.
After about two weeks of investigating this from many angles, I'm pretty sure now that the problem has something to do with Allegro timers and my main loop code.
This is my main game loop (simplifed to highlight essential parts):
bool quitSignal = false;
do {
while (ticks == 0)
rest(1); // yield CPU time
while (ticks > 0)
{
const unsigned int old_ticks = ticks;
// logic step
if (quitSignal = DoLogic())
break;
ticks--;
if (old_ticks <= ticks)
break;
}
// draw step
allegro_gl_set_allegro_mode();
RenderToScreen();
allegro_gl_unset_allegro_mode();
allegro_gl_flip();
} while (!quitSignal);
Here is how I initialize the timer:
install_timer();
LOCK_VARIABLE(ticks);
LOCK_FUNCTION(Ticker);
install_int_ex(Ticker, BPS_TO_TIMER(60));
And this is the callback function that increments the ticks variable:
volatile unsigned int ticks = 0;
void Ticker()
{
ticks++;
} END_OF_FUNCTION(Ticker)
Basically this is the approach recommended in
this article. For the most part, it seems to be working perfectly.
This probably isn't the issue here, but here is how I initialize AllegroGL (i've tried many variations of this as well):
allegro_gl_set(AGL_DOUBLEBUFFER, TRUE);
allegro_gl_set(AGL_COLOR_DEPTH, desktop_color_depth());
allegro_gl_set(AGL_WINDOW_X, (screenW - w)/2);
allegro_gl_set(AGL_WINDOW_Y, (screenH - h)/2);
allegro_gl_set(AGL_SUGGEST, AGL_DOUBLEBUFFER | AGL_COLOR_DEPTH | AGL_WINDOW_X | AGL_WINDOW_Y);
And the fullscreen switching code:
if (set_gfx_mode((fullscreen)?GFX_OPENGL_FULLSCREEN:GFX_OPENGL_WINDOWED,w,h,0,0) < 0)
{
// ... error checking ...
}
Things I tried:- I tried a variant of the game loop that uses a redraw flag to optimize drawing instead of breaking the inner loop if it takes too long, it behaves almost exactly like the one above:
bool quitSignal = false;
bool redraw = true;
do {
while (ticks > 0)
{
// logic step
if (quitSignal = DoLogic())
break;
ticks--;
redraw = true;
}
if (redraw)
{
// draw step
allegro_gl_set_allegro_mode();
RenderToScreen();
allegro_gl_unset_allegro_mode();
allegro_gl_flip();
redraw = false;
}
} while (!quitSignal);
- I tried having a separate "frames" variable in my main loop code and checked against ticks like so:
while (frames < ticks) { ... frames++; }
Supposedly it's not a good idea to change the ticks variable from two places because the operations might be executed at once in two threads. However, this didn't solve my problem and in fact didn't seem to have any effect.
- I tried creating a separate thread that would redraw the screen while logic ran at the same time. I stupidly assumed that somehow this would solve the problem. It didn't.
- I tried switching off hardware acceleration and commented out all the lines concerning AllegroGL. Interestingly enough, the game ran smoothly but this introduced a whole range of new problems. Now, when in fullscreen and with camera scrolling around, the game would either show a tearing scan-line that went from the bottom of the screen toward the top in a kind of regular pattern, or, if I called vsync() every redraw, it kind of lagged like before, only worse and with a longer period. The game did run quite smooth though. If everything else fails, this is my last resort I guess.
- I tried to rearrange the main loop code in many mysterious ways. I won't go into details here because I tried too many things. Strangely enough, this code does both windowed and fullscreen rendering perfectly (so this gives me reason to believe that it
is possible

):
bool quitSignal = false;
do {
while (ticks > 0)
ticks--;
// logic step
if (quitSignal = DoLogic())
break;
// draw step
allegro_gl_set_allegro_mode();
RenderToScreen();
allegro_gl_unset_allegro_mode();
allegro_gl_flip();
} while (!quitSignal);
But now the problem is that it doesn't run with constant speed across different PCs. It runs fine on my desktop PC, on my laptop it runs too fast and in a test VM it runs too slow. It also doesn't respond properly if I make changes to timer BPS. Which I guess it shouldn't. I thought I understood something about game loops, but I have no clue really why the code above won't run in constant speed.
Things I didn't try:- I didn't try implementing the loop using semaphores, as suggested
in this article. I don't have much hope that this would solve the problem though.
So it seems like I have two choices: either to have smooth fullscreen rendering, or to have the game run at constant time. The solution that would make both work eludes me.
Anyway sorry for the lengthy post. I'm hoping that there are some Allegro ninjas here that would help me solve this before I go completely mad.
